In Blazor WebAssembly, how to include hash in static file link/script reference in index.html for cache-busting?

Viewed 1658

In server-side ASP.NET, we can do asp-append-version=true on static assets in a .cshtml file in order to automatically append a hash of the file to the filename. But, in Blazor WebAssembly, this doesn't work, which makes sense because I have a simple index.html file that bootstraps Blazor and references static files, not a server-modified file.

So is there a good way in Blazor WebAssembly's index.html file to append a hash to the URL of a static file, similar in outcome to the old asp-append-version=true? For example, to make <link href="css/site.css" rel="stylesheet" /> become <link href="css/site.css?v=1234abc..." rel="stylesheet" />, and thus changes to site.css on deployment will result in all clients GETting the newly changed static file, rather than relying on cache?

1 Answers

I came up with a workaround for this. I have created a controller that will handle the request, read the index.html, search and replace a "{version}" placeholder and then return the content.

First step is to modify the Startup.cs:

            //endpoints.MapFallbackToFile("index.html");
            endpoints.MapFallbackToController("Index", "Home");

Then create a controller like this one:

public class HomeController : Controller
{
    private static string _processedIndexFile;
    private readonly IWebHostEnvironment _webHostEnvironment;

    public HomeController(IWebHostEnvironment webHostEnvironment)
    {
        _webHostEnvironment = webHostEnvironment;
    }

    [ResponseCache(CacheProfileName = CacheProfileNames.NoCache)]
    public IActionResult Index()
    {
        return ProcessAndReturnIndexFile();
    }

    private IActionResult ProcessAndReturnIndexFile()
    {
        if (_processedIndexFile == null)
        {
            IFileInfo file = _webHostEnvironment.WebRootFileProvider.GetFileInfo("index.html");
            _processedIndexFile = System.IO.File.ReadAllText(file.PhysicalPath);
            _processedIndexFile = _processedIndexFile.Replace("{version}", AppInfo.CompiledOn.ToString("yyyy'-'MM'-'dd'-'HH'-'mm'-'ss"));
        }
        return Content(_processedIndexFile, "text/html");
    }
}

And finally inside the index.html file, use urls like:

<link href="css/app.min.css?v={version}" rel="stylesheet" />

This also allows you to do some other things before returning the file inside the Index method, like checking if falling back to index.html is correct for the current request:

        // Check for incorrect fallback routes
        if (Request.Path.Value?.Contains("api/") == true)
            return NotFound();
Related