Thursday, May 24, 2012

Versioning Static Web Content (css & js) in Asp.Net

Often when building or maintaining a web application the supposedly static content turns out not to be that static in real life. The trouble is that is you have applied an aggressive caching policy to improve client perceived performance by marking the content never expiring, any changed static content will never be pulled down by the browser.

The end user can force the browser to re-download the content, and this is something we all as web developers find ourselves doing several times a day, by using the Ctrl+F5 keys. This will force the browser to re-download all items, however is a standard end user going to know to do this? I think not, and more importantly they shouldn’t have to!

Possible Solutions

While googling for methods to achieve this for a project at work, it became apparent that there are two main possibilities within the Asp.Net framework, one of which being much simpler to achieve. Firstly you could employ the url rewriting module to re-write the urls to include a version string. The new url will fool the browser to request the file when the version changes, however this is going to take a hit for every request to actually perform the re-writing.

A simpler, and as it turns out widely used method (stackoverflow being one example), is to just append a version query-string to the end of the url. Within the Asp.Net MVC framework we have the Url.Content extension that will resolve the provided address to a true absolute address. It is possible to write our own method to perform the same function but also append the version string.

Appending a version string

So we can define our own class with our implementation of the Content method. I have chosen to change the name of the method to VersionedContent, to better describe the purpose of the method; and we should continue to use the existing Content method where we do not require the versioning behaviour, such as when referencing third part libraries that already contain versions within their filenames.

   1:  public static class UrlHelperExtensions
   2:  {
   3:      private static string _versionQueryString;
   4:      
   5:      static UrlHelperExtensions()
   6:      {
   7:          Assembly asembly = Assembly.GetExecutingAssembly();
   8:          AssemblyName name = assembly.GetName();
   9:          _versionQueryString = "?v=" + name.Version.GetHashCode().ToString();
  10:      }
  11:      
  12:      public static string VersionedContent(this UrlHelper urlHelper, string url)
  13:      {
  14:          return UrlHelper = urlHelper.Content(url) + _versionQueryString;
  15:      }
  16:  }



Then in the page/view we just need to include the relevent namespace and replace the use of Url.Content with Url.VersionedContent.


Razor View Engine (MVC)


   1:  <link type="text/css" rel="styleSheet" href="@Url.Content("~/content/css/style-base.css")" />

 


Standard View Engine and Regular Aspx Pages


   1:  <link type="text/css" rel="styleSheet" href="<%=Url.Content("~/content/css/style-base.css")%>" />



 


Summary


So as you can see it is actually a simple change to your views to ensure that the static content can be versioned. These links can also be applied to other forms of static content such as images, however images generally do stay more static than the js and css files, so generally this is not required.


One thing to note is that the method of generating the version string based on the version of the assembly is only going to work if the version of the assembly is being changed! I would strongly recommend that you define a version numbering scheme ensure that the version number is changed each time the site is changed.

1 comment: