强制浏览器在asp.net应用程序中获取最新的js和css文件

一些浏览器cachingjs和css文件,除非你强制它们,否则无法刷新它们。 什么是最简单的方法。

我刚刚实施了这个似乎工作的解决scheme。

在页面上声明一个版本variables

public string version { get; set; } 

从web.config中获取版本号

  version = ConfigurationManager.AppSettings["versionNumber"]; 

在你的aspx页面调用javascript和样式表就像这样

 <script src="scripts/myjavascript.js?v=<%=version %>" type="text/javascript"></script> <link href="styles/mystyle.css?v=<%=version %>" rel="stylesheet" type="text/css" /> 

所以,如果你在你的web.config中设置了1.0版本的版本,你的浏览器将会下载最新的文件,希望能够为你和你的用户节省一些沮丧。

还有另一种解决scheme效果更好,还是会导致网站出现无法预料的问题?

我通过将最后修改的时间戳添加为脚本的查询参数来解决此问题。

我用扩展方法做了这个,并在我的CSHTML文件中使用它。 注意:这个实现caching了1分钟的时间戳,所以我们不会把磁盘安静得这么厉害。

这是扩展方法:

 public static class JavascriptExtension { public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename) { string version = GetVersion(helper, filename); return MvcHtmlString.Create("<script type='text/javascript' src='" + filename + version + "'></script>"); } private static string GetVersion(this HtmlHelper helper, string filename) { var context = helper.ViewContext.RequestContext.HttpContext; if (context.Cache[filename] == null) { var physicalPath = context.Server.MapPath(filename); var version = $"?v={new System.IO.FileInfo(physicalPath).LastWriteTime.ToString("MMddHHmmss")}"; context.Cache.Add(filename, version, null, DateTime.Now.AddMinutes(5), TimeSpan.Zero, CacheItemPriority.Normal, null); return version; } else { return context.Cache[filename] as string; } } } 

然后在CSHTML页面中:

  @Html.IncludeVersionedJs("/MyJavascriptFile.js") 

在呈现的HTML中,它显示为:

  <script type='text/javascript' src='/MyJavascriptFile.js?20111129120000'></script> 

你的解决scheme有效 事实上,这很受欢迎。

即使Stack Overflow也使用类似的方法:

 <link rel="stylesheet" href="http://sstatic.net/so/all.css?v=6184"> 

其中v=6184可能是SVN修订号。

如果你为你的JS / CSS使用bundle,ASP.NET MVC将为你处理这个问题。 它会自动附加一个GUIDforms的版本号到你的软件包,并且只有当软件包被更新时(也就是说任何源文件有改变)才更新这个GUID。

这也有助于如果你有大量的JS / CSS文件,因为它可以大大提高内容加载时间!

看这里

ASP.NET Core (MVC 6)中,通过asp-append-version标签帮助程序可以开箱即用:

 <script src="scripts/myjavascript.js" asp-append-version="true"></script> <link href="styles/mystyle.css rel="stylesheet" asp-append-version="true" /> 

在asp.net中有一个内置的方法: 打包 。 只要使用它。 每个新版本都有唯一的后缀“?v = XXXXXXX”。 在debugging模式下,绑定closures,用于在web.config中打开make设置:

 <system.web> <compilation debug="false" /> </system.web> 

或者添加到方法RegisterBundles(BundleCollection包):

 BundleTable.EnableOptimizations = true; 

例如:

BundleConfig.cs:

 bundles.Add(new ScriptBundle("~/Scripts/myjavascript.js") .Include("~/Scripts/myjavascript.js")); bundles.Add(new StyleBundle("~/Content/mystyle.css") .Include("~/Content/mystyle.css")); 

_Layout.cshtml:

 @Scripts.Render("~/Scripts/myjavascript.js") @Styles.Render("~/Content/mystyle.css") 

有趣的是,这个网站与您在代理设置方面描述的方法有问题,即使它应该是安全的。

检查这个元堆栈溢出的讨论。

所以有鉴于此,不使用GET参数来更新是有意义的,而是实际的文件名:

 href="/css/scriptname/versionNumber.css" 

即使这是更多的工作要做,因为您将不得不实际创build该文件,或者为其构buildURL重写。

根据Adam Tegan的回答 ,修改用于Web表单应用程序。

在.cs类代码中:

 public static class FileUtility { public static string SetJsVersion(HttpContext context, string filename) { string version = GetJsFileVersion(context, filename); return filename + version; } private static string GetJsFileVersion(HttpContext context, string filename) { if (context.Cache[filename] == null) { string filePhysicalPath = context.Server.MapPath(filename); string version = "?v=" + GetFileLastModifiedDateTime(context, filePhysicalPath, "yyyyMMddhhmmss"); return version; } else { return string.Empty; } } public static string GetFileLastModifiedDateTime(HttpContext context, string filePath, string dateFormat) { return new System.IO.FileInfo(filePath).LastWriteTime.ToString(dateFormat); } } 

在aspx标记中:

 <script type="text/javascript" src='<%= FileUtility.SetJsVersion(Context,"/js/exampleJavaScriptFile.js") %>'></script> 

在呈现的HTML中,它显示为

 <script type="text/javascript" src='/js/exampleJavaScriptFile.js?v=20150402021544'></script> 

这里有一个与ASP.NET 5 / MVC 6 / vNext兼容的方法。

第1步:创build一个类来返回文件的最后写入时间,类似于这个线程中的其他答案。 请注意,这需要ASP.NET 5(或其他)依赖项注入。

 public class FileVersionService { private IHostingEnvironment _hostingEnvironment; public FileVersionService(IHostingEnvironment hostingEnvironment) { _hostingEnvironment = hostingEnvironment; } public string GetFileVersion(string filename) { var path = string.Format("{0}{1}", _hostingEnvironment.WebRootPath, filename); var fileInfo = new FileInfo(path); var version = fileInfo.LastWriteTimeUtc.ToString("yyyyMMddhhmmssfff"); return version; } } 

步骤2:startup.cs里注册要注入的服务:

 public void ConfigureServices(IServiceCollection services) { ... services.AddScoped<FileVersionService>(); ... } 

步骤3:然后,在ASP.NET 5中,可以将服务直接注入到布局视图中,如_Layout.cshtml,如下所示:

 @inject Namespace.Here.FileVersionService fileVersionService <!DOCTYPE html> <html lang="en" class="@ViewBag.HtmlClass"> <head> ... <link href="/css/styles.css?v=@fileVersionService.GetFileVersion("\\css\\styles.css")" rel="stylesheet" /> ... </head> <body> ... </body> 

还有一些最后的方法可以将物理path更好地结合起来,并且以更符合语法的风格处理文件名,但这是一个起点。 希望它能帮助人们转向ASP.NET 5。

对于这个问题,答案比这个问题给出的答案要简单一些(方法是一样的):

在web.config中定义密钥:

 <add key="VersionNumber" value="06032014"/> 

直接从aspx页面调用appsettings:

 <link href="styles/navigation.css?v=<%=ConfigurationManager.AppSettings("VersionNumber")%>" rel="stylesheet" type="text/css" /> 

从上面的答案开始,我修改了一小段代码,使帮助程序也可以使用CSS文件,并且每次在文件中进行一些更改时添加一个版本,而不仅仅是当您进行构build时

 public static class HtmlHelperExtensions { public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename) { string version = GetVersion(helper, filename); return MvcHtmlString.Create("<script type='text/javascript' src='" + filename + version + "'></script>"); } public static MvcHtmlString IncludeVersionedCss(this HtmlHelper helper, string filename) { string version = GetVersion(helper, filename); return MvcHtmlString.Create("<link href='" + filename + version + "' type ='text/css' rel='stylesheet'/>"); } private static string GetVersion(this HtmlHelper helper, string filename) { var context = helper.ViewContext.RequestContext.HttpContext; var physicalPath = context.Server.MapPath(filename); var version = "?v=" + new System.IO.FileInfo(physicalPath).LastWriteTime .ToString("yyyyMMddHHmmss"); context.Cache.Add(physicalPath, version, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero, CacheItemPriority.Normal, null); if (context.Cache[filename] == null) { context.Cache[filename] = version; return version; } else { if (version != context.Cache[filename].ToString()) { context.Cache[filename] = version; return version; } return context.Cache[filename] as string; } } } 

我在我的aspnet MVC 4网站中采用了稍微不同的技术:

_ViewStart.cshtml:

 @using System.Web.Caching @using System.Web.Hosting @{ Layout = "~/Views/Shared/_Layout.cshtml"; PageData.Add("scriptFormat", string.Format("<script src=\"{{0}}?_={0}\"></script>", GetDeployTicks())); } @functions { private static string GetDeployTicks() { const string cacheKey = "DeployTicks"; var returnValue = HttpRuntime.Cache[cacheKey] as string; if (null == returnValue) { var absolute = HostingEnvironment.MapPath("~/Web.config"); returnValue = File.GetLastWriteTime(absolute).Ticks.ToString(); HttpRuntime.Cache.Insert(cacheKey, returnValue, new CacheDependency(absolute)); } return returnValue; } } 

然后在实际的观点中:

  @Scripts.RenderFormat(PageData["scriptFormat"], "~/Scripts/Search/javascriptFile.min.js") 

获取文件修改时间,如下所示

 private static string GetLastWriteTimeForFile(string pathVal) { return System.IO.File.GetLastWriteTime(HostingEnvironment.MapPath(pathVal)).ToFileTime().ToString(); } 

追加这个input作为查询string

 public static string AppendDateInFile(string pathVal) { var patheWithDate = new StringBuilder(pathVal); patheWithDate.AppendFormat("{0}x={1}", pathVal.IndexOf('?') >= 0 ? '&' : '?', GetLastWriteTimeForFile(pathVal)); return patheWithDate.ToString(); } 

从标记调用这个。

MVC扩展助手方法

添加一个扩展方法

 namespace TNS.Portal.Helpers { public static class ScriptExtensions { public static HtmlString QueryStringScript<T>(this HtmlHelper<T> html, string path) { var file = html.ViewContext.HttpContext.Server.MapPath(path); DateTime lastModified = File.GetLastWriteTime(file); TagBuilder builder = new TagBuilder("script"); builder.Attributes["src"] = path + "?modified=" + lastModified.ToString("yyyyMMddhhmmss"); return new HtmlString(builder.ToString()); } public static HtmlString QueryStringStylesheet<T>(this HtmlHelper<T> html, string path) { var file = html.ViewContext.HttpContext.Server.MapPath(path); DateTime lastModified = File.GetLastWriteTime(file); TagBuilder builder = new TagBuilder("link"); builder.Attributes["href"] = path + "?modified=" + lastModified.ToString("yyyyMMddhhmmss"); builder.Attributes["rel"] = "stylesheet"; return new HtmlString(builder.ToString()); } } } 

在web.config中添加这个名称空间

 <system.web.webPages.razor> <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <pages pageBaseType="System.Web.Mvc.WebViewPage"> <namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Optimization"/> <add namespace="System.Web.Routing" /> <add namespace="TNS.Portal" /> <add namespace="TNS.Portal.Helpers" /> </namespaces> </pages> </system.web.webPages.razor> 

在视图中使用它

 @Html.QueryStringScript("/Scripts/NPIAjaxCalls.js") @Html.QueryStringStylesheet("/Content/StyledRadio.css") 

简化了之前的build议并为.NET Web Forms开发人员提供了代码。

这将接受资源的文件path中的相对(“〜/”)和绝对URL。

放入一个静态扩展类文件,如下所示:

 public static string VersionedContent(this HttpContext httpContext, string virtualFilePath) { var physicalFilePath = httpContext.Server.MapPath(virtualFilePath); if (httpContext.Cache[physicalFilePath] == null) { httpContext.Cache[physicalFilePath] = ((Page)httpContext.CurrentHandler).ResolveUrl(virtualFilePath) + (virtualFilePath.Contains("?") ? "&" : "?") + "v=" + File.GetLastWriteTime(physicalFilePath).ToString("yyyyMMddHHmmss"); } return (string)httpContext.Cache[physicalFilePath]; } 

然后在您的主页面中调用它:

 <link type="text/css" rel="stylesheet" href="<%= Context.VersionedContent("~/styles/mystyle.css") %>" /> <script type="text/javascript" src="<%= Context.VersionedContent("~/scripts/myjavascript.js") %>"></script> 

这样做的主要问题主要是你需要记住每次你改变你的CSS或JS文件时,更新你的代码的版本号。

一个可能更好的方法是使用每个CSS或js文件设置一个有保证的唯一参数,如下所示:

 <script src="scripts/myjavascript.js?_=<%=DateTime.Now.Ticks%>" type="text/javascript"></script> <link href="styles/mystyle.css?_=<%=DateTime.Now.Ticks%>" rel="stylesheet" type="text/css" /> 

这会强制每次都要从服务器请求文件,这也意味着您的网站在加载页面时不会被执行,因为这些文件永远不会被caching,并且每次都会使用不需要的带宽。

从本质上讲,如果您每次更改版本时都记得更新版本号,则可以避开您正在进行的操作。

基于上面的答案,我写了一个小的扩展类来处理CSS和JS文件:

 public static class TimestampedContentExtensions { public static string VersionedContent(this UrlHelper helper, string contentPath) { var context = helper.RequestContext.HttpContext; if (context.Cache[contentPath] == null) { var physicalPath = context.Server.MapPath(contentPath); var version = @"v=" + new FileInfo(physicalPath).LastWriteTime.ToString(@"yyyyMMddHHmmss"); var translatedContentPath = helper.Content(contentPath); var versionedContentPath = contentPath.Contains(@"?") ? translatedContentPath + @"&" + version : translatedContentPath + @"?" + version; context.Cache.Add(physicalPath, version, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero, CacheItemPriority.Normal, null); context.Cache[contentPath] = versionedContentPath; return versionedContentPath; } else { return context.Cache[contentPath] as string; } } } 

不要写这样的东西:

 <link href="@Url.Content(@"~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content(@"~/Scripts/bootstrap.min.js")"></script> 

你现在可以写:

 <link href="@Url.VersionedContent(@"~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" /> <script src="@Url.VersionedContent(@"~/Scripts/bootstrap.min.js")"></script> 

即简单地用Url.ContentreplaceUrl.VersionedContent

生成的url如下所示:

 <link href="/Content/bootstrap.min.css?v=20151104105858" rel="stylesheet" type="text/css" /> <script src="/Scripts/bootstrap.min.js?v=20151029213517"></script> 

如果使用扩展类,则可能需要添加error handling以防MapPath调用不起作用,因为contentPath不是物理文件。

我使用类似的方法来做同样的事情,而不用修改每个页面。 添加了PreRender事件是主文件。 它保持我的逻辑在一个地方,适用于js和css文件。

 protected void Page_PreRender(object sender, EventArgs e) { HtmlLink link = null; LiteralControl script = null; foreach (Control c in Header.Controls) { //StyleSheet add version if (c is HtmlLink) { link = c as HtmlLink; if (link.Href.EndsWith(".css", StringComparison.InvariantCultureIgnoreCase)) { link.Href += string.Format("?v={0}", ConfigurationManager.AppSettings["agVersion"]); } } //Js add version if (c is LiteralControl) { script = c as LiteralControl; if (script.Text.Contains(".js")) { var foundIndexes = new List<int>(); for (int i = script.Text.IndexOf(".js\""); i > -1; i = script.Text.IndexOf(".js\"", i + 1)) { foundIndexes.Add(i); } for (int i = foundIndexes.Count - 1; i >= 0; i--) { script.Text = script.Text.Insert(foundIndexes[i] + 3, string.Format("?v={0}", ConfigurationManager.AppSettings["agVersion"])); } } } } } 

<?php $rand_no = rand(10000000, 99999999)?> <script src="scripts/myjavascript.js?v=<?=$rand_no"></script>

这适用于所有浏览器中的我。 在这里,我用PHP来产生随机号码。 你可以使用你自己的服务器端语言

您可以覆盖脚本或样式的DefaultTagFormat属性。

 Scripts.DefaultTagFormat = @"<script src=""{0}?v=" + ConfigurationManager.AppSettings["pubversion"] + @"""></script>"; Styles.DefaultTagFormat = @"<link href=""{0}?v=" + ConfigurationManager.AppSettings["pubversion"] + @""" rel=""stylesheet""/>"; 

对于ASP.NET页面,我正在使用以下内容

之前

 <script src="/Scripts/pages/common.js" type="text/javascript"></script> 

后(强制重新加载)

  <script src="/Scripts/pages/common.js?ver<%=DateTime.Now.Ticks.ToString()%>" type="text/javascript"></script> 

添加DateTime.Now.Ticks工作得很好。