Prevent Js and Css Browser Caching Issues with ASP.NET

You’ve seen this problem before- you deploy a new version of your website but the style is off and you’re getting weird javascript errors. You know the issue: Firefox or IE is caching and old version of the css/js file and it’s screwing up the web app. The user needs to clear the cache so the latest version is pulled. The solution: versionstamp your include files!

Take a lesson from Rails and create a helper which appends a stamp to your include files (and takes care of the other required markup). It’s simple- embed the following code in your views:

<%=SiteHelper.JsUrl("global.js") %>

will render

<script type="text/javascript" src="/content/js/global.js?33433651"></script>

The browser will invalidate the cache because of the new query string and you’ll be problem free. Version stamps are better than timestamps because the version will only change if you redeploy your site.

Here’s the code, which is based on the AppHelper for Rob Conery’s Storefront MVC:

using System.Reflection;
using System.Web.Mvc;

namespace ViewSample
{
public static class SiteHelper
{
private static readonly string _assemblyRevision = Assembly.GetExecutingAssembly().GetName().Version.Build.ToString() + Assembly.GetExecutingAssembly().GetName().Version.Revision.ToString();

/// <summary>
/// Returns an absolute reference to the Content directory
/// </summary>
public static string ContentRoot
{
get
{
return "/content";
}
}

/// <summary>
/// Builds a CSS URL with a versionstamp
/// </summary>
/// <param name="cssFile">The name of the CSS file</param>
public static string CssUrl(string cssFile)
{

string result = string.Format("<link rel='Stylesheet' type='text/css' href='{0}/css/{1}?{2}' />", ContentRoot, cssFile, _assemblyRevision);
return result;
}
/// <summary>
/// Builds a js URL with a versionstamp
/// </summary>
/// <param name="cssFile">The name of the CSS file</param>
public static string JsUrl(string jsPath)
{
return string.Format("<script type='text/javascript' src='{0}/js/{1}?{2}'></script>", ContentRoot, jsPath, _assemblyRevision);
}

}
}

  • http://sothy.info Chanty Sothy

    Good solution, but It will not work in Medium Trust share hosting like godaddy, because this code using Reflection “Assembly.GetExecutingAssembly()”.

    Thanks

  • http://www.michaelhamrah.com Michael

    Oooh.. good point. You can always switch the reflection call to a random number set in a static constructor- that way, the random will only be generated once per application initialization. It’s better than a timestamp, which wouldn’t invalidate when you needed it too.

  • Pingback: ASP.NET MVC Archived Blog Posts, Page 1

  • Mike Roosa

    I have added this code to my project and it works great except that _assemblyRevision is always 0. What do I need to do to have it generate the correct number?

  • http://www.michaelhamrah.com Michael

    The problem is the AssemblyVersion attribute isn’t specified correctly. In the AssemblyInfo.cs file change the AssemblyVersion attribute to:

    [assembly: AssemblyVersion("1.0.*")]

    You can also try the random number approach- set _assemblyRevision = new Random(1000).Next(9999) will produce a random number between 1000 and 9999.

  • shaikh

    i try it using
    using following but its not working

    protected void Page_PreInit(object sender, EventArgs e)
    {
    List fileList = new List();
    fileList = new List();
    fileList.Add(“PFR_EOT.js”);
    fileList.Add(“util_ajax.js”);
    fileList.Add(“sessvars.js”);
    fileList.Add(“dataset_filter.js”);
    foreach (string fileName in fileList)
    {
    string dModified = new System.IO.FileInfo(Server.MapPath(fileName)).LastWriteTime.Ticks.ToString();
    string fSrc = fileName + “?t=” + Session["examid"].ToString();
    Page.ClientScript.RegisterClientScriptInclude(this.GetType(), fSrc, fSrc);

    }
    fileList.Clear();
    }