For a very long time we have been telling users, developers, testers and frankly everyone that uses our web application, that they should clear their cache in order to receive the latest version of a JavaScript or Cascading Style Sheet.
This is a very poor solution to a common problem, so what are our options?
- Change the name of the resource file when updated.
- Use Output Caching rules in IIS 7.
- Append a version number to the query string when a file is modified.
Altering the name of a file when it has been changed or updated would be the best solution, but is also the most costly and error prone and for these reasons this option was discarded.
Within IIS 7 output caching rules can be set up to return the latest version of a file once it has been modified, or after a set period of time. This solution appeared the simplest but to be honest was something we knew very little about, and was the reason we didn’t go down this route.
We had already begun appending a version query string manually in some parts of the application and this was the main contributor to selecting this option. However having manually entered a version number myself I know how cumbersome and error prone this is.
The decision was made that we wanted to use the query string to indicate the version number forcing the client to get the latest version of the resource, but we wanted this approach to be automated.
In the system we had a mix of the following script tags:
<script src=”../shared/js/MyJavascript.js" type="text/javascript" language="javascript"></script>
and
<script src=”../shared/js/MyJavascript.js?v=1.0" type="text/javascript" language="javascript"></script>
The second example is what we intend to automate. In order to do this we need a number that increases automatically that can be appended to the query string. The assembly version is suitable as it is managed as part of the build process and can be set to increase automatically. It is also updated as part of our deployment process meaning that the version number will match the version number of the software, this is not required but is kinda neat.
This solution will retrieve not only the modified files, but a new copy of all JS and CSS files after deployment. This will only happen once after deployment so we were happy with this implementation.
Assembly Version
The assembly version can be set within the AssemblyInfo.cs file of any visual studio project, and is located at the bottom of the file.
[assembly: AssemblyVersion("1.0.*")]
The build number can be replaced with a * which will then be updated at build time. The build number is the number of days since January 1, 2000 local time, and the revision is the number of seconds since midnight local time, divided by 2 (as detailed here).
This unique and increasing number can then be appended to the resource URL.
Append the Version Number
A method can then be created to read the version number and append it to the query string.
/// <summary> /// Gets or sets the assembly version. /// </summary> private static string AssemblyVersion { get; set; } /// <summary> /// Appends a version number to thje query string of a resource Url. /// </summary> /// <param name="resourceUrl">The resource Url.</param> /// <returns>The url with the query string of version added.</returns> public static string AppendVersion(string resourceUrl) { if (string.IsNullOrEmpty(AssemblyVersion)) { AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); } return string.Concat(resourceUrl, "?v=", AssemblyVersion); }
This method can now be called when registering your CSS or JS file:
<script language="javascript" src="<%= VresionManager.AppendVersion("../shared/js/MyJavascript.js")%>" type="text/javascript"></script>
Conclusion
This approach offers a simple programmatic solution to version control of JS and CSS files, all be it a slightly overcooked approach. Using output caching rules within IIS 7 is worth exploring if you want a non programmatic approach to this problem.
I received the following comment on http://www.reddit.com/ and wanted to share it with my readers, as it raised a very valid point:
I also wanted to share my reply as I think it contains detail I should have included in the post:
[…] Automating JS and CSS Versioning […]
Great article thanks. I opted to use the date stamp of the actual JS/CSS/Image file in each case. Like you, I didn’t want to leave things to chance so wanted to fix in the application rather than rely on servers being set “correctly”.