From 300c05fc2ebd5dcdc6da7a6325b322157ca152e5 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 17 Oct 2023 17:43:27 +0800 Subject: [PATCH] Add CDN support for bundles. --- .../UI/Bundling/BundleConfigurationContext.cs | 4 +- .../Bundling/BundleConfigurationExtensions.cs | 7 +++ .../BundleContributorCollectionExtensions.cs | 4 ++ .../AspNetCore/Mvc/UI/Bundling/BundleFile.cs | 14 +++++ .../Mvc/UI/Bundling/BundleFileContributor.cs | 14 +++-- .../UI/Bundling/BundleFileListExtensions.cs | 32 +++++++++++ .../Bundling/IBundleConfigurationContext.cs | 2 +- .../Mvc/UI/Bundling/BundleCacheItem.cs | 4 +- .../Mvc/UI/Bundling/BundleManager.cs | 54 +++++++++++++++---- .../Mvc/UI/Bundling/IBundleManager.cs | 4 +- .../TagHelpers/AbpTagHelperResourceService.cs | 34 +++++++----- .../TagHelpers/AbpTagHelperScriptService.cs | 11 ++-- .../TagHelpers/AbpTagHelperStyleService.cs | 16 +++--- .../Mvc/UI/Resources/IWebRequestResources.cs | 3 +- .../Mvc/UI/Resources/WebRequestResources.cs | 7 +-- 15 files changed, 161 insertions(+), 49 deletions(-) create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFile.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileListExtensions.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs index b67c8f6d3d..bc4dc14d11 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; public class BundleConfigurationContext : IBundleConfigurationContext { - public List Files { get; } + public List Files { get; } public IFileProvider FileProvider { get; } @@ -18,7 +18,7 @@ public class BundleConfigurationContext : IBundleConfigurationContext public BundleConfigurationContext(IServiceProvider serviceProvider, IFileProvider fileProvider) { - Files = new List(); + Files = new List(); ServiceProvider = serviceProvider; LazyServiceProvider = ServiceProvider.GetRequiredService(); FileProvider = fileProvider; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationExtensions.cs index 3ee0fe6cd9..ca628f4d67 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; @@ -10,6 +11,12 @@ public static class BundleConfigurationExtensions return bundleConfiguration; } + public static BundleConfiguration AddCdnFiles(this BundleConfiguration bundleConfiguration, params string[] files) + { + bundleConfiguration.Contributors.AddCdnFiles(files.Select(x => new BundleFile(x, true)).ToArray()); + return bundleConfiguration; + } + public static BundleConfiguration AddContributors(this BundleConfiguration bundleConfiguration, params IBundleContributor[] contributors) { Check.NotNull(contributors, nameof(contributors)); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs index dc0ac8bfbb..3c0dfdeab8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs @@ -6,4 +6,8 @@ public static class BundleContributorCollectionExtensions { contributors.Add(new BundleFileContributor(files)); } + public static void AddCdnFiles(this BundleContributorCollection contributors, params BundleFile[] files) + { + contributors.Add(new BundleFileContributor(files)); + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFile.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFile.cs new file mode 100644 index 0000000000..58427b6c40 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFile.cs @@ -0,0 +1,14 @@ +namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +public class BundleFile +{ + public string File { get; set; } + + public bool IsCdn { get; set; } + + public BundleFile(string file, bool isCdn = false) + { + File = file; + IsCdn = isCdn; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs index 7f218e7fbf..5107c006a0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs @@ -1,22 +1,30 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; public class BundleFileContributor : BundleContributor { - public string[] Files { get; } + public List Files { get; } + + public BundleFileContributor(params BundleFile[] files) + { + Files = new List(); + Files.AddRange(files); + } public BundleFileContributor(params string[] files) { - Files = files ?? Array.Empty(); + Files = new List(); + Files.AddRange(files.Select(file => new BundleFile(file))); } public override void ConfigureBundle(BundleConfigurationContext context) { foreach (var file in Files) { - context.Files.AddIfNotContains(x => x.Equals(file, StringComparison.OrdinalIgnoreCase), () => file); + context.Files.AddIfNotContains(x => x.File.Equals(file.File, StringComparison.OrdinalIgnoreCase), () => file); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileListExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileListExtensions.cs new file mode 100644 index 0000000000..b3088b293d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileListExtensions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +/// +/// This class is used to compatible with old code. +/// +public static class BundleFileListExtensions +{ + public static void Add(this List bundleFiles, params string[] files) + { + bundleFiles.AddRange(files.Select(file => new BundleFile(file))); + } + + public static void AddRange(this List bundleFiles, params string[] files) + { + bundleFiles.AddRange(files.Select(file => new BundleFile(file))); + } + + public static void AddIfNotContains(this List bundleFiles, params string[] files) + { + foreach (var file in files) + { + if (!bundleFiles.Any(x => x.File.Equals(file, StringComparison.OrdinalIgnoreCase))) + { + bundleFiles.Add(new BundleFile(file)); + } + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleConfigurationContext.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleConfigurationContext.cs index 61d772d638..96df341514 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleConfigurationContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleConfigurationContext.cs @@ -5,5 +5,5 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; public interface IBundleConfigurationContext : IServiceProviderAccessor { - List Files { get; } + List Files { get; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs index 5c3890a852..f6830cbd74 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs @@ -5,11 +5,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; public class BundleCacheItem { - public List Files { get; } + public List Files { get; } public List WatchDisposeHandles { get; } - public BundleCacheItem(List files) + public BundleCacheItem(List files) { Files = files; WatchDisposeHandles = new List(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs index 8291c2c75f..5a46e1ff5a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs @@ -12,7 +12,6 @@ using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts; using Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles; using Volo.Abp.AspNetCore.Mvc.UI.Resources; -using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.DependencyInjection; using Volo.Abp.VirtualFileSystem; @@ -56,19 +55,22 @@ public class BundleManager : IBundleManager, ITransientDependency Logger = NullLogger.Instance; } - public virtual async Task> GetStyleBundleFilesAsync(string bundleName) + public virtual async Task> GetStyleBundleFilesAsync(string bundleName) { return await GetBundleFilesAsync(Options.StyleBundles, bundleName, StyleBundler); } - public virtual async Task> GetScriptBundleFilesAsync(string bundleName) + public virtual async Task> GetScriptBundleFilesAsync(string bundleName) { return await GetBundleFilesAsync(Options.ScriptBundles, bundleName, ScriptBundler); } - protected virtual async Task> GetBundleFilesAsync(BundleConfigurationCollection bundles, string bundleName, IBundler bundler) + protected virtual async Task> GetBundleFilesAsync(BundleConfigurationCollection bundles, string bundleName, IBundler bundler) { + var result = new List(); + var contributors = GetContributors(bundles, bundleName); + var bundleFiles = RequestResources.TryAdd(await GetBundleFilesAsync(contributors)); var dynamicResources = RequestResources.TryAdd(await GetDynamicResourcesAsync(contributors)); @@ -77,16 +79,48 @@ public class BundleManager : IBundleManager, ITransientDependency return bundleFiles.Union(dynamicResources).ToImmutableList(); } + var localBundleFiles = new List(); + foreach (var bundleFile in bundleFiles) + { + if (bundleFile.IsCdn) + { + if (localBundleFiles.Any()) + { + var cacheItem = AddToBundleCache(bundleName, bundler, localBundleFiles); + result.AddRange(cacheItem.Files); + localBundleFiles.Clear(); + } + + result.Add(bundleFile); + } + else + { + localBundleFiles.Add(bundleFile.File); + } + } + + if (localBundleFiles.Any()) + { + var cacheItem = AddToBundleCache(bundleName, bundler, localBundleFiles); + result.AddRange(cacheItem.Files); + localBundleFiles.Clear(); + } + + return result.Union(dynamicResources).ToImmutableList(); + } + + private BundleCacheItem AddToBundleCache(string bundleName, IBundler bundler, List bundleFiles) + { var bundleRelativePath = Options.BundleFolderName.EnsureEndsWith('/') + bundleName + "." + bundleFiles.JoinAsString("|").ToMd5() + "." + bundler.FileExtension; - var cacheItem = BundleCache.GetOrAdd(bundleRelativePath, () => + return BundleCache.GetOrAdd(bundleRelativePath, () => { var cacheValue = new BundleCacheItem( - new List + new List { - "/" + bundleRelativePath + new BundleFile("/" + bundleRelativePath) } ); @@ -104,8 +138,6 @@ public class BundleManager : IBundleManager, ITransientDependency return cacheValue; }); - - return cacheItem.Files.Union(dynamicResources).ToImmutableList(); } private void WatchChanges(BundleCacheItem cacheValue, List files, string bundleRelativePath) @@ -176,7 +208,7 @@ public class BundleManager : IBundleManager, ITransientDependency } } - protected async Task> GetBundleFilesAsync(List contributors) + protected async Task> GetBundleFilesAsync(List contributors) { var context = CreateBundleConfigurationContext(); @@ -198,7 +230,7 @@ public class BundleManager : IBundleManager, ITransientDependency return context.Files; } - protected virtual async Task> GetDynamicResourcesAsync(List contributors) + protected virtual async Task> GetDynamicResourcesAsync(List contributors) { var context = CreateBundleConfigurationContext(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs index b4ce56cb62..067554e102 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs @@ -5,7 +5,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; public interface IBundleManager { - Task> GetStyleBundleFilesAsync(string bundleName); + Task> GetStyleBundleFilesAsync(string bundleName); - Task> GetScriptBundleFilesAsync(string bundleName); + Task> GetScriptBundleFilesAsync(string bundleName); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs index a247022674..1926cb48cf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs @@ -3,10 +3,12 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; @@ -63,18 +65,24 @@ public abstract class AbpTagHelperResourceService : ITransientDependency foreach (var bundleFile in bundleFiles) { - var file = HostingEnvironment.WebRootFileProvider.GetFileInfo(bundleFile); - - if (file == null || !file.Exists) + if (bundleFile.IsCdn) { - Logger.LogError($"Could not find the bundle file '{bundleFile}' for the bundle '{bundleName}'!"); - AddErrorScript(viewContext, tagHelper, context, output, bundleFile, bundleName!); - continue; + AddHtmlTag(viewContext, tagHelper, context, output, bundleFile, null); } - - if (file.Length > 0) + else { - AddHtmlTag(viewContext, tagHelper, context, output, bundleFile + "?_v=" + file.LastModified.UtcTicks); + var file = HostingEnvironment.WebRootFileProvider.GetFileInfo(bundleFile.File); + if (file == null || !file.Exists) + { + Logger.LogError($"Could not find the bundle file '{bundleFile.File}' for the bundle '{bundleName}'!"); + AddErrorScript(viewContext, tagHelper, context, output, bundleFile, bundleName!); + continue; + } + + if (file.Length > 0) + { + AddHtmlTag(viewContext, tagHelper, context, output, bundleFile, file); + } } } @@ -84,13 +92,13 @@ public abstract class AbpTagHelperResourceService : ITransientDependency protected abstract void CreateBundle(string bundleName, List bundleItems); - protected abstract Task> GetBundleFilesAsync(string bundleName); + protected abstract Task> GetBundleFilesAsync(string bundleName); - protected abstract void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, string file); + protected abstract void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, BundleFile file, IFileInfo? fileInfo = null); - protected virtual void AddErrorScript(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, string file, string bundleName) + protected virtual void AddErrorScript(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, BundleFile file, string bundleName) { - output.Content.AppendHtml($"{Environment.NewLine}"); + output.Content.AppendHtml($"{Environment.NewLine}"); } protected virtual string GenerateBundleName(List bundleItems) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs index 260f28f812..ec6ed2c99c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; @@ -32,12 +33,12 @@ public class AbpTagHelperScriptService : AbpTagHelperResourceService ); } - protected override async Task> GetBundleFilesAsync(string bundleName) + protected override async Task> GetBundleFilesAsync(string bundleName) { return await BundleManager.GetScriptBundleFilesAsync(bundleName); } - protected override void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, string file) + protected override void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, BundleFile file, IFileInfo? fileInfo = null) { var defer = tagHelper switch { @@ -46,12 +47,14 @@ public class AbpTagHelperScriptService : AbpTagHelperResourceService _ => false }; - var deferText = (defer || Options.DeferScriptsByDefault || Options.DeferScripts.Any(x => file.StartsWith(x, StringComparison.OrdinalIgnoreCase))) + var deferText = (defer || Options.DeferScriptsByDefault || Options.DeferScripts.Any(x => file.File.StartsWith(x, StringComparison.OrdinalIgnoreCase))) ? "defer" : string.Empty; var nonceText = (viewContext.HttpContext.Items.TryGetValue(AbpAspNetCoreConsts.ScriptNonceKey, out var nonce) && nonce is string nonceString && !string.IsNullOrEmpty(nonceString)) ? $"nonce=\"{nonceString}\"" : string.Empty; - output.Content.AppendHtml($"{Environment.NewLine}"); + + var src = file.IsCdn ? file.File : viewContext.GetUrlHelper().Content((file.File + "?_v=" + fileInfo!.LastModified.UtcTicks).EnsureStartsWith('~')); + output.Content.AppendHtml($"{Environment.NewLine}"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs index 0f68433f4c..a43221841e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Security; @@ -18,7 +19,7 @@ public class AbpTagHelperStyleService : AbpTagHelperResourceService public AbpTagHelperStyleService( IBundleManager bundleManager, IOptions options, - IWebHostEnvironment hostingEnvironment, + IWebHostEnvironment hostingEnvironment, IOptions securityHeadersOptions) : base( bundleManager, options, @@ -36,12 +37,12 @@ public class AbpTagHelperStyleService : AbpTagHelperResourceService ); } - protected override async Task> GetBundleFilesAsync(string bundleName) + protected override async Task> GetBundleFilesAsync(string bundleName) { return await BundleManager.GetStyleBundleFilesAsync(bundleName); } - protected override void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, string file) + protected override void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, BundleFile file, IFileInfo? fileInfo = null) { var preload = tagHelper switch { @@ -50,15 +51,16 @@ public class AbpTagHelperStyleService : AbpTagHelperResourceService _ => false }; - if (preload || Options.PreloadStylesByDefault || Options.PreloadStyles.Any(x => file.StartsWith(x, StringComparison.OrdinalIgnoreCase))) + var href = file.IsCdn ? file.File : viewContext.GetUrlHelper().Content((file.File + "?_v=" + fileInfo!.LastModified.UtcTicks).EnsureStartsWith('~')); + if (preload || Options.PreloadStylesByDefault || Options.PreloadStyles.Any(x => file.File.StartsWith(x, StringComparison.OrdinalIgnoreCase))) { output.Content.AppendHtml(SecurityHeadersOptions.UseContentSecurityPolicyScriptNonce - ? $"{Environment.NewLine}" - : $"{Environment.NewLine}"); + ? $"{Environment.NewLine}" + : $"{Environment.NewLine}"); } else { - output.Content.AppendHtml($"{Environment.NewLine}"); + output.Content.AppendHtml($"{Environment.NewLine}"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/IWebRequestResources.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/IWebRequestResources.cs index 2eefbcc0dd..9479dec8ce 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/IWebRequestResources.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/IWebRequestResources.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; namespace Volo.Abp.AspNetCore.Mvc.UI.Resources; public interface IWebRequestResources { - List TryAdd(List resources); + List TryAdd(List resources); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs index 3e046499ab..e68b671fc6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs @@ -1,23 +1,24 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Http; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Mvc.UI.Resources; public class WebRequestResources : IWebRequestResources, IScopedDependency { - protected Dictionary> Resources { get; } + protected Dictionary> Resources { get; } protected IHttpContextAccessor HttpContextAccessor { get; } public WebRequestResources(IHttpContextAccessor httpContextAccessor) { HttpContextAccessor = httpContextAccessor; - Resources = new Dictionary>(); + Resources = new Dictionary>(); } - public List TryAdd(List resources) + public List TryAdd(List resources) { var path = HttpContextAccessor.HttpContext?.Request?.Path ?? "";