4

I'm trying to reload reviewed css/js files automatically to prevent cache. I can loop trough header (which is runat="server") and add a value to link href references but the loop doesn't catch script tags as controls.

if (Page.Header != null)
{
    foreach (var sc in Page.Header.Controls)
    {
        var type = sc.GetType();

        if (type == typeof(HtmlLink))
        {
            ((HtmlLink)sc).Href += "?v=" + Tools.BuildDate();
        }
        else if (type == typeof(HtmlGenericControl))
        {
            ((HtmlGenericControl)sc).Attributes["src"] += "?v=" + Tools.BuildDate();
        }
    }
}

I've tried to add runat="server" to script tags but in this way asp.net tries to compile those files and build fails. I cannot add version every time because the project has many files. Also code blocks is not allowed when runat="server" is applied.

I want to use a commonly accepted solution so I don't have to worry for css/js file versions any more.

4
  • You could add your own HtmlGenericControl that represent your script tag to the Page.Header.Controls collection, so it will be rendered by ASP.NET Commented Oct 24, 2016 at 6:17
  • @SimonMourier any example? Commented Oct 24, 2016 at 14:54
  • something like this: pastebin.com/0ugCENqg Commented Oct 25, 2016 at 4:56
  • check this stackoverflow.com/questions/4841455 as well Commented Oct 26, 2016 at 11:59

4 Answers 4

3

In MVC Core 1.0 there is a tag helper for this exact purpose. If you are using a different version of MVC then the previous answers should help.

<link rel="stylesheet" src="wherever.css" asp-append-version="true" />
Sign up to request clarification or add additional context in comments.

Comments

2

A script tag is not going to be picked up as a Control, so you need to wrap it in a custom control if you want to handle it in the controller. Or you could just add the timestamp directly in the view:

<script type="text/javascript" src="path/to/file.js?v=<%= Tools.BuildDate() %>" />

Here is some more info: http://madskristensen.net/post/cache-busting-in-aspnet

Comments

2

This should do

<script src="ScriptsFolder/ScriptFile.js?v=<%=Tools.BuildDate() %>"></script>
<link href="StylesFolder/StyleFile.css?v=<%= Tools.BuildDate() %>" rel="stylesheet" />

When the value returned by Tools.BuildDate() changes, the browser will be forced to reload the script/style file since the resource url will have changed e.g. from url/ScriptsFolder/ScriptFile.js?v=1234 to url/ScriptsFolder/ScriptFile.js?v=5678. This by far should be the simplest and fastest solution, since it will only be appending the query string v=value to the resource url. The appended querystring will completely be ignored by the server when fetching the resource specified.

Comments

1

Without knowing what ver of ASP.NET (or MVC) you are using, I can give you an easy way. You can always build a "helper" to do the heavy lifting for you. This is the way that MVC and ASP.NET 5 helpers handle it, but will work with just about any version. I generally don't prefer inline code, but a static helper can be made safe to protect against runtime errors.

First build a static helper utility:

public static class ScriptHelper
{
    public static IHtmlString Render(params string[] paths)
    {
        return RenderFormat(DefaultTagFormat, paths);
    }

    public static IHtmlString RenderFormat(string tagFormat, params string[] paths)
    {
        if (string.IsNullOrEmpty(tagFormat))
        {
            throw new ArgumentException("Tag Format cannot be null");
        }
        if (paths == null)
        {
            throw new ArgumentNullException("Paths cannot be empty");
        }
        if (paths.Any(string.IsNullOrEmpty))
        {
            throw new ArgumentException("paths");
        }
        return BuildHtml(tagFormat, paths);
    }

    private static string defaultFormat = "<script src=\"{0}?ver={1}\"></script>";

    public static string DefaultTagFormat
    {
        get
        {
            return defaultFormat;
        }
        set { defaultFormat = value; }
    }

    private static IHtmlString BuildHtml(string tagFormat, params string[] paths)
    {
        StringBuilder builder = new StringBuilder();
        foreach (string path in paths)
        {
            StringBuilder builder = new StringBuilder();
        foreach (string path in paths)
        {
            // Substitute your logic for version number
            var version = "1234";
            // You could factory this to a concrete type based on file extension etc.
            var fileToOutPut = new VersionedJsFile(path,version,tagFormat);
            builder.Append(fileToOutPut.RenderOutput());
            builder.Append(Environment.NewLine);
        }
        return new HtmlString(builder.ToString());
    }

}

You can add extra logic to wrap around the path, validation of path, virtual paths, etc. Best to create an object with a render method that you can pass in the path (from the helper) and the version. This is the oop way to do it and is more flexible. Doing that you can then have this be a "VersionHelper" instead and have it deal with css/js/etc.

 internal abstract class HtmlFile
{
    public abstract string RenderOutput();
}

internal class VersionedJsFile : HtmlFile
{

    private string _version;
    private string _path;
    private string _format;
    public VersionedJsFile(
        string path, 
        string version,
        string format)
    {
        if (version != null) _version = version;
        if (path != null) _path = path;
        if(!string.IsNullOrEmpty(format))
            _format = format;
    }

    public override string RenderOutput()
    {
        if (!string.IsNullOrEmpty(_path)
            && !string.IsNullOrEmpty(_format))
        {
            string versionedFilePath = string.Format(_format, _path, _version);
            return versionedFilePath;

        }
        return string.Empty;
    }
}

Then add the helper to your page/master page/layout:

 <%: ScriptHelper.Render("/scripts/bootstrap.js") %>

As you can see, the helper takes a params object so you can apply to multiple files at once.

Output:

<script src="/scripts/bootstrap.js?ver=1234"></script>

The helper can easily be enhanced.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.