在我注意到捆绑的脚本和CSS文件返回cache: no-cache
,expires: -1
和pragma: no-cache
标头之前,一切都很棒。
当然,这与Azure无关。为了证明这一点,我通过直接从我的网站访问它来测试捆绑,而不是CDN - 即。的 mysite.com/bundles/mybundle?v= {myassemblyversion} 即可。结果是一样的。当我禁用CDN并使用MVC生成的v
查询字符串访问捆绑文件时,标头符合预期:公共缓存,到期时间为一年。
我尝试实现IBundleTransform
接口,但context.BundleVirtualPath
是只读的(即使它说获取或设置虚拟路径......)。我还尝试修改Application_EndRequest()
的响应标头,但它也没有用。我的最后一个赌注是编写IIS出站规则,但由于我的bundle(与“custom”v查询字符串一起使用)不返回Last-Modified
标题,所以这也是徒劳的尝试。
我的问题是:如果我希望将捆绑的文件缓存在客户端上,也就是说,直到v
查询字符串更改,我如何使用Azure CDN绑定MVC?
答案 0 :(得分:1)
我知道我在游戏中有点晚了,但我确实找到了解决方法。我正在利用Frison B Alexander here的代码。
问题是,一旦为StyleBundles或ScriptBundles重写查询字符串,一年的默认缓存行为将重置为no-cache。这可以通过重新生成MVC框架在指定每个Bundle的CDNPath时使用的每个包完全相同的查询字符串来解决。
以下是使用MVC Web App模板完成的工作。这是BundleConfig类:
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
//we have to go ahead and add our Bundles as if there is no CDN involved.
//this is because the bundle has to already exist in the BundleCollection
//in order to get the hash that the MVC framework will generate for the
//querystring.
Bundle jsBundle = new ScriptBundle("~/scripts/js3").Include(
"~/Scripts/jquery-{version}.js",
"~/Scripts/modernizr-*",
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js");
bundles.Add(jsBundle);
Bundle cssBundle = new StyleBundle("~/content/css3").Include(
"~/Content/bootstrap.css",
"~/Content/site.css");
bundles.Add(cssBundle);
#if Debug
bundles.UseCdn = false;
#else
bundles.UseCdn = true;
//grab our base CDN hostname from web.config...
string cdnHost = ConfigurationManager.AppSettings["CDNHostName"];
//get the hashes that the MVC framework will use per bundle for
//the querystring.
string jsHash = GetBundleHash(bundles, "~/scripts/js3");
string cssHash = GetBundleHash(bundles, "~/content/css3");
//set up our querystring per bundle for the CDN path.
jsBundle.CdnPath = cdnHost + "/scripts/js3?v=" + jsHash;
cssBundle.CdnPath = cdnHost + "/content/css3?v=" + cssHash;
#endif
}
//Frison B Alexander's code:
private static string GetBundleHash(BundleCollection bundles, string bundlePath)
{
//Need the context to generate response
var bundleContext = new BundleContext(new HttpContextWrapper(HttpContext.Current), BundleTable.Bundles, bundlePath);
//Bundle class has the method we need to get a BundleResponse
Bundle bundle = BundleTable.Bundles.GetBundleFor(bundlePath);
var bundleResponse = bundle.GenerateBundleResponse(bundleContext);
//BundleResponse has the method we need to call, but its marked as
//internal and therefor is not available for public consumption.
//To bypass this, reflect on it and manually invoke the method
var bundleReflection = bundleResponse.GetType();
var method = bundleReflection.GetMethod("GetContentHashCode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
//contentHash is whats appended to your url (url?###-###...)
var contentHash = method.Invoke(bundleResponse, null);
return contentHash.ToString();
}
}