MVC4脚本捆绑缓存问题

8
我们有一个MVS应用程序,使用Bundle类捆绑javascript代码(不进行缩小)。捆绑工作正常,但当我们运行应用程序时,缓存值设置为Cache-Control:no-cache,同时每次刷新页面时请求总是返回200 OK。这意味着即使没有更改,js也没有被缓存在客户端上。 此外,是否有一种方法可以验证捆绑的js是动态构建的还是从服务器缓存获取的? 谢谢。

不确定这是否有帮助,但您可能想尝试使用http://getcassette.net。我认为它非常好用且易于使用。 - Daniel Lidström
谢谢你的回复,但我想知道为什么在MVC 4 Web应用程序中捆绑(Bundling)不起作用。 - Bhargava
3个回答

9

我看到了与Codeplex链接中提到的问题相同的行为:

i.e. if I visit these URLs in the following order then the behaviour is -

bundle.css?v=1234 : no-cache
bundle.css : public
bundle.css?v=1234 : public
我决定深入了解一下System.Web.Optimization的源代码,看看发生了什么。在Bundle类上,有一个设置头信息的私有方法,它似乎陷入了这段代码中:
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方法的访问级别被标记为internal,因此需要复制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
@Simon_Weaver,我们的捆绑配置代码需要进行代码更改。我已经更新了答案,包括这一部分。希望能有所帮助。 - Soldarnal
1
您先生真是位绅士和学者。由于捆绑引擎中未记录的怪异操作,这确实是一个十分棘手的难题。我很惊讶没有更多人注意到这一点。当启用CDN URL时,版本会被移除,并且如果哈希值不正确,则响应标头将设置为nocache,这意味着自制版本查询字符串附加将无法正常工作。 - eatfrog
2
你也可以通过将查询字符串参数从“v”更改为其他内容,例如“version”,来完成这个操作。 - Ankush Jain

2
问题似乎出在Microsoft.AspNet.Web.Optimization NuGet包上。将版本从1.3.0降级到1.1.0后,一切似乎正常工作。
在codeplex上的博客文章中提到了同样的问题:链接

只是澄清一下,1.1.0 可以正常工作,1.1.1 似乎是引入了错误的版本。该错误特别影响部署到不同服务器的捆绑包。如果在本地运行,则看不到该错误。 - Worthy7

2

由于之前的答案没有帮助到我(不确定后果),我找到了一个解决此问题的方法。

问题在于,正如在这里已经说明的那样,当您在查询字符串上发送?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的说明进行操作,谢谢! - Miha Pirnat
@walter,您能详细解释一下这个部署问题吗?我现在就有这个问题,但它只发生在已部署的版本中,我的本地开发版本始终正常工作(即使在部署版本配置上运行)。这个哈希问题可能与此相关吗?谢谢。 - Worthy7
没关系,这是该死的优化包来自1.1.1版本。 - Worthy7

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接