Thứ Hai, 11 tháng 5, 2015

Force reload of JS and CSS files with ASP.NET MVC 3


Caching is a good thing. It allows better performance and saves bandwidth. However, some browsers tend to use the outdated cached version of CSS and JavaScript files even when the file was modified on the server. That leads to bad user experiences and bugs that can be sometimes hard to find.
There is a technique to force browsers to download the latest files, It consists of appending a query string to the request, for example:

<link href="style.css?foo=bar"></link>

If your server isn't set up to do anything with the query string, everything after the ? will be ignored. But the name will look like a new file to the browser. This technique is used on Stackoverflow, if you look at the source code of every page, you can see:

<link rel="stylesheet" type="text/css" href="http://cdn.sstatic.net/stackoverflow/all.css?v=daf5ad3f0583">

The ideal would not be to force the browser to reload the file on every launch of the site/application, but only when you roll out a new version.
Here is what we want:
  • Let the browser download the files (CSS & JS) and cache them
  • Use the cached version if it hasn't changed
  • Force the browser to get the new version of the files when a new version is available
Here is the solution I came up with for an ASP.NET MVC project: I use the assembly version number as the querystring parameter, so that every time a new version of my app is built and deployed, I can be sure that the client downloads the latest version of the files.
Here are the required steps:
  1. We want the assembly version number to change every time we build. To do that, we need to go to the Properties folder of the MVC project and edit the AssemblyInfo.cs file. For example:Change
    [assembly: AssemblyVersion("1.0.0.0")]
    To

    [assembly: AssemblyVersion("1.0.0.*")]
  2. Create the UrlHelper extensions that will append the version number as a querystring

    public static class FileAutoVersioningHelper
    {
    private readonly static string _version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
    public static string GeneratePath(string fileName)
    {
    return string.Format("{0}?v={1}", fileName, _version);
    }
    }

    public static class UrlHelperExtensions
    {
    public static string Stylesheet(this UrlHelper helper, string fileName)
    {
    return helper.Content(string.Format("~/Content/{0}", FileAutoVersioningHelper.GeneratePath(fileName)));
    }

    public static string Script(this UrlHelper helper, string fileName)
    {
    return helper.Content(string.Format("~/Scripts/{0}", FileAutoVersioningHelper.GeneratePath(fileName)));
    }
    }
  3. Add the helper's namespace in the web.config to avoid to have to add it on every view

    <system.web.webPages.razor>
    <
    host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <
    pages pageBaseType="System.Web.Mvc.WebViewPage">
    <
    namespaces>
    <
    add namespace="System.Web.Mvc" />
    <
    add namespace="System.Web.Mvc.Ajax" />
    <
    add namespace="System.Web.Mvc.Html" />
    <
    add namespace="System.Web.Routing" />
    <
    add namespace="MyApp.HtmlHelpers" />
    </
    namespaces>
    </
    pages>
    </
    system.web.webPages.razor>
  4. Make the change in the views

    <link href="@Url.Stylesheet("Site.css")" rel="stylesheet" type="text/css" />
Here is the generated html :
<link href="/Content/Site.css?v=1.0.0.27717" rel="stylesheet" type="text/css" />

0 nhận xét:

Đăng nhận xét