Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service/extension for getting a cache-busting 'version string'

Background

I have an application where 3 views utilize html5 offline app functionality. As such, I have an app manifest generated in a razor view. A cut-down version of this view may look like the following:

CACHE MANIFEST

CACHE:
/site.min.css
/site.min.js

In order for the offline app to function correctly, the cached files must exactly match those requested by offline pages within the app. However, I would like to apply a 'cache-busting' version string to the js/css resources referenced in this manifest. For HTML tags, this is supported by the ScriptTagHelper but I have not found any helpers/extension methods which support this for plain URLs (as required in the above manifest).

With reference to this post, I have resolved this by injecting a FileVersionProvider into my manifest view and using the AddFileVersionToPath() method as follows:

@inject FileVersionProvider versionProvider
CACHE MANIFEST

CACHE:
@versionProvider.AddFileVersionToPath("/site.min.css")
@versionProvider.AddFileVersionToPath("/site.min.js")

However, the FileVersionProvider class is in the Microsoft.AspNetCore.Mvc.TagHelpers.Internal namespace which does not fill me with confidence form a maintenance perspective.

Finally, my implementation of the DI setup for this is not exactly ideal (see below). I don't like the fact that I need to make calls to GetService() and surely I should be specifying a specific MemoryCache?

services.AddSingleton<FileVersionProvider>(s => 
    new FileVersionProvider(
        s.GetService<IHostingEnvironment>()?.WebRootFileProvider, 
        s.GetService<IMemoryCache>(), 
        new PathString("") ));

Question

Has anyone had a requirement to create a link to a js/css resource with a version string before? If so, is there a more elegant solution?

like image 679
SpruceMoose Avatar asked Nov 08 '22 14:11

SpruceMoose


1 Answers

Did you try this extension method? It worked for me on ASP.NET Core 2.1 LTS without any depencency injection, just the extension method. Should work in 2.0 as well if you still use it.

I had a similar use-case where SCEditor loads CSS from a path passed by JavaScript.

@{
    // Without version hash cache buster, the editor internally cache the request and we don't get the new stylesheet - even not with CTRL + F5
    string editorThemePath = this.AddFileVersionToPath("/css/my-editor-theme.css");
}
<script>
      sceditor.create(editorTextarea, {
            // ...
            style: '@editorThemePath'
      })
</script>

Generated HTML

<script>
      sceditor.create(editorTextarea, {
            style: '/css/my-editor-theme.css?v=sRU_qBg7xyQElPLskjekOlvSHdRF2Ap1leTP8ui4CaI',
      })
</script>

Keep in mind that this implementation requires paths relative to wwwroot. So in this case, /css/my-editor-theme.css is in wwwroot/css/my-editor-theme.css.

Could be changed if you replace the first parameter of FileVersionProvider from hostingEnvironment.WebRootFileProvider to hostingEnvironment.ContentRootFileProvider. But I havent' tested what are the side effects and security concerns of this, so you should stay on wwwroot if possible since this folder is designed to be public accessable.

like image 138
Lion Avatar answered Nov 14 '22 22:11

Lion