【问题标题】:MVC4 Script Bundles Caching issueMVC4 脚本包缓存问题
【发布时间】:2017-03-16 04:21:44
【问题描述】:

我们有一个 MVS 应用程序,我们在其中使用 Bundle 类捆绑 javascript 代码(不要进行缩小)。

Bundling 工作得很好,但是当我们运行应用程序时,缓存值设置为 Cache-Control:no-cache 并且每次我们刷新页面时,请求总是有一个200 好的。这意味着即使没有任何更改,js 也不会缓存在客户端上。

还有没有办法验证捆绑的js是动态构建的还是从服务器缓存中获取的?

谢谢

【问题讨论】:

  • 不确定这是否有用,但您可能想尝试getcassette.net。我只是觉得它很好用,而且很容易使用。
  • 谢谢 Daniel,但我想知道为什么捆绑在 MVC 4 Web 应用程序中不起作用。

标签: c# asp.net asp.net-mvc-4 bundles


【解决方案1】:

我看到的行为与提到该问题的 codeplex 链接中描述的行为相同:

即如果我按以下顺序访问这些 URL,则行为是 -

bundle.css?v=1234 : no-cache
bundle.css : public
bundle.css?v=1234 : public

我决定深入研究 System.Web.Optimization 源代码,看看发生了什么。在Bundle类上,有一个私有方法设置headers,貌似落入了这段代码中:

if (noCache) {
    cachePolicy.SetCacheability(HttpCacheability.NoCache);
}

noCache 变量是通过参数设置的。本例调用方法为设置:

// Set to no-cache if the version requested does not match
bool noCache = false;
var request = context.HttpContext.Request;
if (request != null) {
    string queryVersion = request.QueryString.Get(VersionQueryString);
        if (queryVersion != null && bundleResponse.GetContentHashCode() != queryVersion) {
                noCache = true;
    }
}

长话短说,我们已经切换到使用 Azure CDN 作为我们的捆绑包,并根据程序集版本将版本查询字符串参数更改为 ?v=1.0.0.0(类似于 this question)。捆绑代码将“1.0.0.0”与捆绑内容的 SHA256 哈希码进行比较,结果将捆绑标记为无缓存。

我通过更新查询字符串以匹配内容哈希解决了这个问题。

不幸的是,GetContentHashCode 方法的访问级别被标记为内部,因此必须复制 implementation。我最终创建了一个从 Bundle 继承的类,以便它可以将版本号作为转换应用到 CdnPath:

public class ProxiedCdnBundle : Bundle
{
    private readonly string _cdnHost;

    public ProxiedCdnBundle(string virtualPath, string cdnHost = "")
        : base(virtualPath)
    {
        _cdnHost = cdnHost;
    }

    public override BundleResponse ApplyTransforms(BundleContext context, string bundleContent, IEnumerable<BundleFile> bundleFiles)
    {
        var response = base.ApplyTransforms(context, bundleContent, bundleFiles);

        if (context.BundleCollection.UseCdn && !String.IsNullOrWhiteSpace(_cdnHost))
        {
            string path = System.Web.VirtualPathUtility.ToAbsolute(context.BundleVirtualPath);
            base.CdnPath = string.Format("{0}{1}?v={2}", _cdnHost, path, GetBundleHash(response));
        }

        return response;
    }


    private static string GetBundleHash(BundleResponse response)
    {
        using (var hashAlgorithm = CreateHashAlgorithm())
        {
            return HttpServerUtility.UrlTokenEncode(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(response.Content)));
        }
    }

    private static SHA256 CreateHashAlgorithm()
    {
        if (CryptoConfig.AllowOnlyFipsAlgorithms)
        {
            return new SHA256CryptoServiceProvider();
        }

        return new SHA256Managed();
    }
}

【讨论】:

  • 如何更新查询字符串以匹配内容哈希?您是重新编译源代码还是只是更改了 URL 的生成方式?
  • @Simon_Weaver 它需要对我们的捆绑配置代码进行代码更改。我已经更新了答案以包括该部分。希望对您有所帮助。
  • 您先生是一位绅士和一位学者。由于捆绑引擎中未记录的怪异,这是一个非常难以破解的问题。我很惊讶没有更多的人注意到这一点。启用cdn url时版本被删除的事实,以及如果哈希不正确,响应标头如何设置为nocache,这意味着自制的版本控制查询字符串附加将不起作用。
  • 您也可以通过将查询字符串参数从“v”更改为其他内容,即“版本”
【解决方案2】:

问题似乎与 Microsoft.AspNet.Web.Optimization NuGet 包有关。将版本从 1.3.0 降级到 1.1.0 后,一切似乎都运行良好。

Link to blog post on codeplex which mentioned the same issue

【讨论】:

  • 澄清一下,1.1.0 有效,1.1.1 似乎是引入错误的地方。该错误特别影响部署到不同服务器的捆绑包。本地运行看不到这个bug。
【解决方案3】:

由于上述答案对我没有帮助(不确定后果),因此我找到了解决此问题的方法。

问题是,正如这里已经说明的那样,当您在查询字符串上发送 ?v 并且值与实际哈希不匹配时,它将返回 no-cache

不发送任何东西都不是一个选项(缓存可能永远不会过期)。 发送缓存破坏参数也不是一种选择。如果您这样做并且您有多个实例,则可能会在部署期间缓存错误的值(如果您不从负载均衡器中删除旧实例)。

要解决此问题,只需将 UseCdn 设置为 false 并在捆绑配置期间更改以下内容:

Scripts.DefaultTagFormat = string.Format(@"<script src=""{0}{{0}}""></script>", CdnRoot);

希望,我有帮助。

【讨论】:

  • 这解决了 Cache-Control: private 的问题,请按照 Integrate ASP.NET bundling and minification with Azure CDN 的说明进行操作,谢谢!
  • @walter 你能解释一下这个部署问题吗?我现在有这个问题,但它只发生在已部署的版本上,我的本地开发版本总是可以正常工作(即使在部署版本配置上运行)。这个散列问题可能有某种关联吗?谢谢
  • 没关系,这是该死的 1.1.1 优化包
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-11
  • 1970-01-01
  • 2012-11-12
  • 1970-01-01
相关资源
最近更新 更多