4

我一直在關注這個教程:https://azure.microsoft.com/en-us/documentation/articles/cdn-serve-content-from-cdn-in-your-web-application/MVC 5捆綁和Azure的CDN(查詢字符串)

的一切都是偉大的,直到我注意到,捆綁的腳本和CSS文件與cache: no-cacheexpires: -1pragma: no-cache頭返回。 當然,這與Azure無關。爲了證明這一點,我通過直接從我的網站訪問它們來測試捆綁包,而不是CDN - 即。 mysite.com/bundles/mybundle?v={myassemblyversion}。結果是一樣的。當我禁用CDN,並使用由MVC生成的v查詢字符串訪問捆綁文件時,標題符合預期:公共緩存,到期時間爲一年。

我試圖實現IBundleTransform接口,但context.BundleVirtualPath是隻讀的(即使它說獲取或設置虛擬路徑...)。我也嘗試修改Application_EndRequest()上的響應標題,但它也不起作用。我最後一個打賭是編寫IIS出站規則,但由於我的包(用於「自定義」v查詢字符串)不返回Last-Modified標頭,這也是徒勞的嘗試。

我的問題是:如果我想要將我的捆綁文件緩存在客戶端上 - 也就是說,直到v查詢字符串更改,我怎樣才能使用MVC捆綁Azure CDN?

回答

1

我知道我對比賽有點遲,但我找到了解決方法。我正在使用Frison B Alexander的代碼here

問題是,一旦您重寫StyleBundles或ScriptBundles的查詢字符串,一年的默認緩存行爲將重置爲no-cache。這是通過重新生成每個捆綁包完全相同的查詢字符串來解決的,MVC框架在指定每個捆綁包的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(); 
     } 
    }