ResourceLocation s verzí jako je asp-append-version="true" v MVC

Ahoj,
potřeboval jsem zajistit, aby odkazy na vlastní aplikační scriptz a css byly v release generovány s verzí (hash) jako do dělá tag helper asp-append-version=“true” v MVC. Jestli jsem něco nepřehlédl, tak DotVVM toto umí jen u EmbeddedResourceLocation ale u UrlResourceLocation to není.

Napsal jsem si vlastní třídu UrlFileVersionResourceLocation, kde jsem udělal logiku podobné té co mají v MVC LinkTagHelper.

Koukněte na to, zda by to nešlo dát přímo jako součást do DotVVM.

using System.Security.Cryptography;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.FileProviders;
using DotVVM.Framework.ResourceManagement;

public class UrlFileVersionResourceLocation : IResourceLocation
{
    #region member varible and default property initialization
    private static readonly char[] QueryStringAndFragmentTokens = new char[2] { '?', '#' };

    public string Url { get; }
    public string? DebugUrl { get; set; }
    #endregion

    #region constructors and destructors
    public UrlFileVersionResourceLocation(string url, string? debugUrl = null)
    {
        this.Url = url;
        this.DebugUrl = debugUrl;
    }
    #endregion

    #region action methods
    public string GetUrl(IDotvvmRequestContext context, string name)
    {
        var hostingEnvironment = context.Services.GetRequiredService<IWebHostEnvironment>();
        var fileProvider = hostingEnvironment.WebRootFileProvider;

        string url = context.TranslateVirtualPath(context.Configuration.Debug ? this.DebugUrl ?? this.Url : this.Url);

        return context.Configuration.Debug ? url : AddFileVersionToPath(fileProvider, context.HttpContext.Request.PathBase, url);
    }
    #endregion

    #region private member functions
    private static string AddFileVersionToPath(IFileProvider fileProvider, IPathString requestPathBase, string path)
    {
        ArgumentNullException.ThrowIfNull(path);

        string filePath = path;
        int index = path.IndexOfAny(QueryStringAndFragmentTokens);
        if (index != -1)
        {
            filePath = path[..index];
        }

        if (Uri.TryCreate(filePath, UriKind.Absolute, out var result) && !result.IsFile)
        {
            return path;
        }

        var fileInfo = fileProvider.GetFileInfo(filePath);
        if (!fileInfo.Exists && requestPathBase.HasValue() && filePath.StartsWith(requestPathBase.Value!, StringComparison.OrdinalIgnoreCase))
        {
            string filePath2 = filePath[requestPathBase.Value!.Length..];
            fileInfo = fileProvider.GetFileInfo(filePath2);
        }

        string value = !fileInfo.Exists ? path : QueryHelpers.AddQueryString(path, "v", GetHashForFile(fileInfo));
        return value;
    }

    private static string GetHashForFile(IFileInfo fileInfo)
    {
        using (SHA256 sHA = SHA256.Create())
        {
            using (Stream inputStream = fileInfo.CreateReadStream())
            {
                return WebEncoders.Base64UrlEncode(sHA.ComputeHash(inputStream));
            }
        }
    }
    #endregion
}

Ve frameworku je FileResourceLocation, která se chová cca stejně jako EmbeddedResourceLocation. Občas tedy nejde použít – když se odkazuje na nějaké další resourcy, protože ten resource přesune taky pod /dotvvmResource/... místo toho aby ho tahala jako static file.

OK,
Tu FileResourceLocation jsem příhlíd, nicméně ta moje je také alternativa.

Díky.

1 Like