3

Possible Duplicate:
force browsers to get latest js and css files in asp.net application

I'm working with someone else's code, so I don't know the whole picture, and I don't even know MVC that well, but here's the problem...

In Site.Master there's a

<%= Html.IncludeJs("ProductPartial")%>

which produces this line in the final mark-up

<script type="text/javascript" src="/Scripts/release/ProductPartial.js"></script>

I made some changes in the JS file, but the old one is obviously cached by the browser, so the changes won't show up until the user refreshes. The usual workaround is to add a version tag at the end of the script source path, but I'm not sure how to do that in this case.

Any suggestions?

Community
  • 1
  • 1
Edgar
  • 4,348
  • 4
  • 40
  • 59

5 Answers5

5

Why not write your own Html helper extension method, and make it output the version number of your application assembly? Something along these lines should do the trick:

public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename)
{
    var version = Assembly.GetExecutingAssembly().GetName().Version;
    return MvcHtmlString.Create(filename + "?v=" + version);
}

You can then increment the version number of the assembly whenever you release a new version to your users, and their caches will be invalidated across the application.

Jonas Høgh
  • 10,358
  • 1
  • 26
  • 46
  • Might be useful. Will see if I can use that instead of Html.IncludeJs. – Edgar Apr 13 '10 at 14:42
  • I'd advise to use a property to only get the executing assembly version once. Otherwise it will be getting that information (which is not particularly fast) once for every script you include on every page. – smdrager Aug 09 '13 at 12:56
  • @smdrager Sure, or just use script bundling if you have many scripts and want the versioning issue handled automatically – Jonas Høgh Aug 09 '13 at 15:52
5

I solved this by tacking a last modified timestamp as a query parameter to the scripts.

I did this with an extension method, and using it in my CSHTML files. Note: this implementation caches the timestamp for 1 minute so we don't thrash the disk quite so much.

Here is the extension method:

 public static class JavascriptExtension {
    public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename) {
        string version = GetVersion(helper, filename);
        return MvcHtmlString.Create("<script type='text/javascript' src='" + filename + version + "'></script>");
    }

    private static string GetVersion(this HtmlHelper helper, string filename)
    {
        var context = helper.ViewContext.RequestContext.HttpContext;

        if (context.Cache[filename] == null) {
            var physicalPath = context.Server.MapPath(filename);
            var version = "?v=" +
              new System.IO.FileInfo(physicalPath).LastWriteTime
                .ToString("yyyyMMddhhmmss");
            context.Cache.Add(physicalPath, version, null,
              DateTime.Now.AddMinutes(1), TimeSpan.Zero,
              CacheItemPriority.Normal, null);
            context.Cache[physicalPath] = version;
            return version;
        }
        else {
            return context.Cache[filename] as string;
        }
    }

And then in the CSHTML page:

 @Html.IncludeVersionedJs("/MyJavascriptFile.js")

In the rendered HTML, this appears as:

 <script type='text/javascript' src='/MyJavascriptFile.ks?20111129120000'></script>
Adam Tegen
  • 25,378
  • 33
  • 125
  • 153
1

Here are some links already on this topic:

Why do some websites access specific versions of a CSS or JavaScript file using GET parameters?

force browsers to get latest js and css files in asp.net application

Community
  • 1
  • 1
David
  • 15,150
  • 15
  • 61
  • 83
  • Not the same thing. I know how to version normal script includes, but the question is about Html.IncludeJs. – Edgar Apr 13 '10 at 14:58
0

Your version strategy really isn't important. As long as the file name is different, the browser will be forced to get the new script. So even this would work:

<%= Html.IncludeJs("ProductPartialv1")%>

ProductPartialv1.js

I have been using this technique for important JavaScript and CSS changes (CSS is also cached by the browser) - so I update the template to use the newer version and I'm safe in the knowledge that if the new HTML is used, so is the new script and CSS file.

It is "in action" on http://www.the-mag.me.uk/ - where I just increment a numeric suffix on the files.

Fenton
  • 241,084
  • 71
  • 387
  • 401
  • That would mean I have to rename the files every time something changes and update all references. Not a perfect solution. – Edgar Apr 13 '10 at 12:03
  • Or you can delete the file from your browser cache.. which browser are you using? – moi_meme Apr 13 '10 at 12:18
  • This is about forcing users' browsers to update, not my own. – Edgar Apr 13 '10 at 14:41
  • On the site mentioned in my example, I rename the file and update the reference once in code. When you say "update all references" - it sounds like you should be holding the script name as a config item, rather than hard-coding it in many places inside your app. This is the only way to force a browser to use the new file. – Fenton Apr 14 '10 at 07:36
0

It turns out IncludeJs is a helper method for automatically including compressed JS files when in release mode: LINK.

So I just have to modify that method a bit to include a version number. Sorry about the confusion.

Edgar
  • 4,348
  • 4
  • 40
  • 59
  • the link you provided is broken, can you please update your answer new link. – Konammagari Jun 24 '15 at 06:14
  • Cached version of that link: http://webcache.googleusercontent.com/search?q=cache:OMvTd05XoEMJ:codebetter.com/karlseguin/2008/12/29/compressing-js-files-as-part-of-your-build-process/+&cd=3&hl=en&ct=clnk&gl=lv – Edgar Jun 28 '15 at 13:54