Search code examples
fileasp.net-corefileprovider

Any FileProvider in ASP.NET Core to provide versioned files by folder instead of parameter?


I need to version my Javacript files (for clearing cache purpose) but cannot use asp-append-version because the script files are used from Javascript import:

import * as Foo from './foo.js'

Therefore, I plan to have a FileProvider that can serve a file with request like /js/v1.0/app.js (and so the foo.js would be serve from /js/v1.0/foo.js). It's okay to serve /v1.0/js/main.js, as long as the relative path is maintained.

I tried this:

        app.UseStaticFiles(new StaticFileOptions()
        {
            RequestPath = "/v*",
        });
        app.UseStaticFiles();

But it wouldn't work, RequestPath does not support wildcard.

Is there a way to do that without a custom Middleware? A FileProvider middleware is quite overkill for this in my opinion. This is my current temporary solution:

    public static IApplicationBuilder UseVersionedScripts(this IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            if (context.Request.Path.HasValue &&
                context.Request.Path.Value.ToLower().StartsWith("/js/v"))
            {
                var filePath = context.Request.Path.Value.Split('/');


                // Write the file to response and return if file exist
            }

            await next.Invoke();
        });

        return app;
    }

Edit: I think in my case, if FileProvider is not supported, a Controller Action maybe better than middleware because PhysicalFile method can take care of the writing.


Solution

  • I made a reusable FileProvider that "transforms" (removes) the version path: https://github.com/BibliTech/VersionedFileProvider

    var versionedFileProvider = new VersionedFileProvider(env.WebRootFileProvider);
    app.UseStaticFiles(new StaticFileOptions()
    {
        FileProvider = versionedFileProvider,
    });
    

    OLD ANSWER:

    In the end I use an Action to serve the file:

    [ApiController]
    public class FileController : ControllerBase
    {
    
        IHostEnvironment env;
        public FileController(IHostEnvironment env)
        {
            this.env = env;
        }
    
        [HttpGet, Route("/js/{version}/{**path}")]
        public IActionResult JavascriptFile(string version, string path)
        {
            var filePath = Path.Combine(
                env.ContentRootPath,
                @"wwwroot\js",
                path);
    
            if (System.IO.File.Exists(filePath))
            {
                return this.PhysicalFile(filePath, "application/javascript");
            }
    
            return this.NotFound();
        }
    
    }