Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Optimization Framework (JS and CSS minification and Bundling) using CDN and HashContent (Cache Buster or Finger Print)

!!! CAUTION!!!

The accepted answer is good, but if you have a high traffic website there's a chance of attaching v= multiple times. The code contains a checker.

I've been looking for any examples or references where ASP.NET Optimization Framework is used with UseCDN = true and HashContent Number is attached to the Bundles' URI. Unfortunately without any luck. The following is a simplified example of my code.

My Bundling code is pretty Simple

        bundles.UseCdn = true;

        BundleTable.EnableOptimizations = true;


        var stylesCdnPath = "http://myCDN.com/style.css";
        bundles.Add(new StyleBundle("~/bundles/styles/style.css", stylesCdnPath).Include(
            "~/css/style.css"));

I call the Render from a Master page

 <%: System.Web.Optimization.Styles.Render("~/bundles/styles/style.css")%>

The generated code is

 <link href="http://myCDN.com/style.css" rel="stylesheet"/>

If I disable UseCDN

 /bundles/styles/style.css?v=geCEcmf_QJDXOCkNczldjY2sxsEkzeVfPt_cGlSh4dg1

How can I make the bunlding add v= Hash Content when useCDN is set to true ?

Edit:

I tried using

 <%: System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/bundles/styles/style.css",true)%> 

it still will not generate v = hash if CdnUse = true

like image 583
Roman Mik Avatar asked Apr 17 '14 23:04

Roman Mik


1 Answers

You can't, Setting UseCdn to true means ASP.NET will serve bundles as is from your CDN Path, no Bundling and Minification will be performed.

The query string v has a value token that is a unique identifier used for caching. As long as the bundle doesn't change, the ASP.NET application will request the bundle using this token. If any file in the bundle changes, the ASP.NET optimization framework will generate a new token, guaranteeing that browser requests for the bundle will get the latest bundle.

Have a look at BundleCollection.ResolveBundleUrl Implementation:

// System.Web.Optimization.BundleCollection
/// <summary>Returns the bundle URL for the specified virtual path, including a content hash if requested.</summary>
/// <returns>The bundle URL or null if the bundle cannot be found.</returns>
/// <param name="bundleVirtualPath">The virtual path of the bundle.</param>
/// <param name="includeContentHash">true to include a hash code for the content; otherwise, false. The default is true.</param>
public string ResolveBundleUrl(string bundleVirtualPath, bool includeContentHash)
{
    Exception ex = ExceptionUtil.ValidateVirtualPath(bundleVirtualPath, "bundleVirtualPath");
    if (ex != null)
    {
        throw ex;
    }
    Bundle bundleFor = this.GetBundleFor(bundleVirtualPath);
    if (bundleFor == null)
    {
        return null;
    }
    if (this.UseCdn && !string.IsNullOrEmpty(bundleFor.CdnPath))
    {
        return bundleFor.CdnPath;
    }
    return bundleFor.GetBundleUrl(new BundleContext(this.Context, this, bundleVirtualPath), includeContentHash);
}

Ref: http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification

Update

You can have the V hash manually added to your CDN by implementing your own bundle and generate the hash on ApplyTransforms call:

public class myStyleBundle: StyleBundle
{
  public myStyleBundle(string virtualPath)
    :base(virtualPath)
  {          
  }

  public myStyleBundle(string virtualPath, string cdnPath)
    : base(virtualPath,cdnPath)
  {
    MyCdnPath = cdnPath;
  }

  public string MyCdnPath
  {
    get;
    set;
  }

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

    base.CdnPath = string.Format("{0}?v={1}", this.MyCdnPath, this.HashContent(response));

    return response;
  }

  private string HashContent(BundleResponse response)
  {
    string result;
    using (SHA256 sHA = new SHA256Managed())
    {
      byte[] input2 = sHA.ComputeHash(Encoding.Unicode.GetBytes(response.Content));
      result = HttpServerUtility.UrlTokenEncode(input2);
    }
    return result;
  }

}

Then, simply do:

bundles.Add(new myStyleBundle("~/bundles/styles/style.css", stylesCdnPath).Include(
           "~/css/style.css"));

Please note that System.Web.Optimization.BundleResponse creates a hash algorithm based on environment settings:

// System.Web.Optimization.BundleResponse
private static SHA256 CreateHashAlgorithm()
{
  if (BundleResponse.AllowOnlyFipsAlgorithms)
  {
     return new SHA256CryptoServiceProvider();
  }
  return new SHA256Managed();
}
like image 139
MK. Avatar answered Nov 15 '22 04:11

MK.