7

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

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

还有一种方法可以验证捆绑的 js 是动态构建的还是从服务器缓存中获取的?

谢谢

4

3 回答 3

8

我看到的行为与提到该问题的 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 之类的东西(类似于这个问题)。捆绑代码将“1.0.0.0”与捆绑内容的 SHA256 哈希码进行比较,结果将捆绑标记为无缓存。

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

不幸的是,GetContentHashCode 方法的访问级别被标记为内部的,因此有必要复制实现。我最终创建了一个从 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();
    }
}
于 2016-07-14T18:03:29.157 回答
2

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

链接到提到相同问题的 codeplex 上的博客文章

于 2014-04-15T04:26:57.693 回答
2

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

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

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

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

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

希望,我有帮助。

于 2017-03-15T20:21:44.260 回答