MVC4 StyleBundle:你可以在debugging模式下添加caching查询string吗?

我有一个MVC应用程序,我使用StyleBundle类来渲染出如下的CSS文件:

 bundles.Add(new StyleBundle("~/bundles/css").Include("~/Content/*.css")); 

我遇到的问题是,在Debug模式下,CSSurl单独渲染,我有一个网页代理,积极caching这些url。 在Release模式下,我知道一个查询string被添加到最终的URL,使每个版本的任何caching失效。

是否有可能configurationStyleBundleDebug模式添加一个随机的查询string以及产生以下输出来解决caching问题?

 <link href="/stylesheet.css?random=some_random_string" rel="stylesheet"/> 

您可以创build一个自定义IBundleTransform类来执行此操作。 这是一个例子,它将使用文件内容的散列来追加av = [filehash]参数。

 public class FileHashVersionBundleTransform: IBundleTransform { public void Process(BundleContext context, BundleResponse response) { foreach(var file in response.Files) { using(FileStream fs = File.OpenRead(HostingEnvironment.MapPath(file.IncludedVirtualPath))) { //get hash of file contents byte[] fileHash = new SHA256Managed().ComputeHash(fs); //encode file hash as a query string param string version = HttpServerUtility.UrlTokenEncode(fileHash); file.IncludedVirtualPath = string.Concat(file.IncludedVirtualPath, "?v=", version); } } } } 

然后,您可以通过将其添加到捆绑的Transforms集合来注册该类。

 new StyleBundle("...").Transforms.Add(new FileHashVersionBundleTransform()); 

现在版本号只会在文件内容改变时才会改变。

你只需要一个唯一的string。 它不一定是哈希。 我们使用该文件的LastModifieddate并从那里获取Ticks。 @Todd指出,打开和阅读这个文件是很昂贵的。 滴答就足以输出一个唯一的数字,当文件被改变时,这个数字就会改变。

 internal static class BundleExtensions { public static Bundle WithLastModifiedToken(this Bundle sb) { sb.Transforms.Add(new LastModifiedBundleTransform()); return sb; } public class LastModifiedBundleTransform : IBundleTransform { public void Process(BundleContext context, BundleResponse response) { foreach (var file in response.Files) { var lastWrite = File.GetLastWriteTime(HostingEnvironment.MapPath(file.IncludedVirtualPath)).Ticks.ToString(); file.IncludedVirtualPath = string.Concat(file.IncludedVirtualPath, "?v=", lastWrite); } } } } 

以及如何使用它:

 bundles.Add(new StyleBundle("~/bundles/css") .Include("~/Content/*.css") .WithLastModifiedToken()); 

这就是MVC写的:

 <link href="bundles/css/site.css?v=635983900813469054" rel="stylesheet"/> 

脚本包也能正常工作。

该库可以在debugging模式下将caching清除哈希添加到您的包文件,以及其他一些caching破坏的东西: https : //github.com/kemmis/System.Web.Optimization.HashCache

您可以将HashCache应用于BundlesCollection中的所有包

所有捆绑包已添加到集合后 ,在BundlesCollection实例上执行ApplyHashCache()扩展方法。

 BundleTable.Bundles.ApplyHashCache(); 

或者你可以将HashCache应用到一个Bundle

创buildHashCacheTransform的实例,并将其添加到要应用HashCache的包实例。

 var myBundle = new ScriptBundle("~/bundle_virtual_path").Include("~/scripts/jsfile.js"); myBundle.Transforms.Add(new HashCacheTransform()); 

我有同样的问题,但升级后,客户端浏览器中的caching版本。 我的解决scheme是在我自己的渲染器中调用@Styles.Render("~/Content/css")在我们的查询string中追加版本号,如下所示:

  public static IHtmlString RenderCacheSafe(string path) { var html = Styles.Render(path); var version = VersionHelper.GetVersion(); var stringContent = html.ToString(); // The version should be inserted just before the closing quotation mark of the href attribute. var versionedHtml = stringContent.Replace("\" rel=", string.Format("?v={0}\" rel=", version)); return new HtmlString(versionedHtml); } 

然后在我看来,我喜欢这样做:

 @RenderHelpers.RenderCacheSafe("~/Content/css") 

目前还不是,但预计会很快添加(现在计划在1.1版本中发布,您可以在这里跟踪这个问题: Codeplex

注意这是为脚本编写的,但也适用于样式(只需更改这些关键词)

build立在@ Johan的答案:

 public static IHtmlString RenderBundle(this HtmlHelper htmlHelper, string path) { var context = new BundleContext(htmlHelper.ViewContext.HttpContext, BundleTable.Bundles, string.Empty); var bundle = System.Web.Optimization.BundleTable.Bundles.GetBundleFor(path); var html = System.Web.Optimization.Scripts.Render(path).ToString(); foreach (var item in bundle.EnumerateFiles(context)) { if (!html.Contains(item.Name)) continue; html = html.Replace(item.Name, item.Name + "?" + item.LastWriteTimeUtc.ToString("yyyyMMddHHmmss")); } return new HtmlString(html); } public static IHtmlString RenderStylesBundle(this HtmlHelper htmlHelper, string path) { var context = new BundleContext(htmlHelper.ViewContext.HttpContext, BundleTable.Bundles, string.Empty); var bundle = System.Web.Optimization.BundleTable.Bundles.GetBundleFor(path); var html = System.Web.Optimization.Styles.Render(path).ToString(); foreach (var item in bundle.EnumerateFiles(context)) { if (!html.Contains(item.Name)) continue; html = html.Replace(item.Name, item.Name + "?" + item.LastWriteTimeUtc.ToString("yyyyMMddHHmmss")); } return new HtmlString(html); } 

用法:

 @Html.RenderBundle("...") @Html.RenderStylesBundle("...") 

更换

 @Scripts.Render("...") @Styles.Render("...") 

优点:

  • 适用于System.Web.Optimizations的v1.0.0.0
  • 适用于捆绑包中的多个文件
  • 获取每个文件的文件修改date而不是散列,而不是一个组

此外,当您需要快速解决schemeBundler:

 public static MvcHtmlString ResolveUrl(this HtmlHelper htmlHelper, string url) { var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext); var resolvedUrl = urlHelper.Content(url); if (resolvedUrl.ToLower().EndsWith(".js") || resolvedUrl.ToLower().EndsWith(".css")) { var localPath = HostingEnvironment.MapPath(resolvedUrl); var fileInfo = new FileInfo(localPath); resolvedUrl += "?" + fileInfo.LastWriteTimeUtc.ToString("yyyyMMddHHmmss"); } return MvcHtmlString.Create(resolvedUrl); } 

用法:

 <script type="text/javascript" src="@Html.ResolveUrl("~/Scripts/jquery-1.9.1.min.js")"></script> 

更换:

 <script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")"></script> 

(也取代了许多其他的替代查找)