From e0dbe3f31d0aec9f7643a8031b4f37b6a4024a98 Mon Sep 17 00:00:00 2001 From: ns8482e <23270244+ns8482e@users.noreply.github.com> Date: Sun, 15 Aug 2021 16:53:28 -0500 Subject: [PATCH] Use of IPostConfigureOptions to configure StaticFileOptions Fixes #6505 #2966 --- .../ConfigureModuleStaticFileOptions.cs | 69 +++++++++++++++++++ .../Extensions/ServiceCollectionExtensions.cs | 25 ++----- src/docs/reference/core/Modules/README.md | 5 +- 3 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 src/OrchardCore/OrchardCore/Modules/Extensions/ConfigureModuleStaticFileOptions.cs diff --git a/src/OrchardCore/OrchardCore/Modules/Extensions/ConfigureModuleStaticFileOptions.cs b/src/OrchardCore/OrchardCore/Modules/Extensions/ConfigureModuleStaticFileOptions.cs new file mode 100644 index 00000000000..3d9ce03e416 --- /dev/null +++ b/src/OrchardCore/OrchardCore/Modules/Extensions/ConfigureModuleStaticFileOptions.cs @@ -0,0 +1,69 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using OrchardCore.Environment.Shell.Configuration; +using OrchardCore.Modules; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Configures Module Static File Providers and Set Cache + /// + internal class ConfigureModuleStaticFileOptions : IPostConfigureOptions + { + private readonly IModuleStaticFileProvider _fileProvider; + private readonly IShellConfiguration _shellConfiguration; + private readonly IWebHostEnvironment _environment; + public ConfigureModuleStaticFileOptions(IModuleStaticFileProvider fileProvider, + IShellConfiguration shellConfiguration, + IWebHostEnvironment environment) + { + _fileProvider = fileProvider; + _shellConfiguration = shellConfiguration; + _environment = environment; + } + public void PostConfigure(string name, StaticFileOptions options) + { + name = name ?? throw new ArgumentNullException(nameof(name)); + options = options ?? throw new ArgumentNullException(nameof(options)); + + if (name != Microsoft.Extensions.Options.Options.DefaultName) + { + return; + } + + var serveWebRoot = _shellConfiguration.GetValue("StaticFileOptions:ServeHostWebRoot", false); + if (serveWebRoot) + { + // Serve Tenant Static files from module and from HostWebRoot + options.FileProvider = new CompositeFileProvider(_fileProvider, _environment.WebRootFileProvider); + } + else + { + // Serve Tenant Static files only from module + options.FileProvider = _fileProvider; + } + + var cacheControl = _shellConfiguration.GetValue("StaticFileOptions:CacheControl", $"public, max-age={TimeSpan.FromDays(30).TotalSeconds}, s-max-age={TimeSpan.FromDays(365.25).TotalSeconds}"); + var beforePrepare = options.OnPrepareResponse; + + options.OnPrepareResponse = ctx => OnPrepareResponseCacheControl(ctx, cacheControl, beforePrepare); + } + + private static void OnPrepareResponseCacheControl(StaticFileResponseContext ctx, string cacheControl, Action beforePrepare) + { + if (beforePrepare != null) + { + beforePrepare(ctx); + } + + // Cache static files for a year as they are coming from embedded resources and should not vary + ctx.Context.Response.Headers[HeaderNames.CacheControl] = cacheControl; + } + } +} diff --git a/src/OrchardCore/OrchardCore/Modules/Extensions/ServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore/Modules/Extensions/ServiceCollectionExtensions.cs index ca24e42c9db..aa062560d57 100644 --- a/src/OrchardCore/OrchardCore/Modules/Extensions/ServiceCollectionExtensions.cs +++ b/src/OrchardCore/OrchardCore/Modules/Extensions/ServiceCollectionExtensions.cs @@ -20,12 +20,10 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; -using Microsoft.Net.Http.Headers; using OrchardCore; using OrchardCore.Environment.Extensions; using OrchardCore.Environment.Shell; using OrchardCore.Environment.Shell.Builders; -using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Environment.Shell.Descriptor.Models; using OrchardCore.Environment.Shell.Models; using OrchardCore.Localization; @@ -173,6 +171,7 @@ private static void AddExtensionServices(OrchardCoreBuilder builder) /// private static void AddStaticFiles(OrchardCoreBuilder builder) { + var services = builder.ApplicationServices; builder.ConfigureServices(services => { services.AddSingleton(serviceProvider => @@ -201,28 +200,14 @@ private static void AddStaticFiles(OrchardCoreBuilder builder) { return serviceProvider.GetRequiredService(); }); + + // Configures StaticFileOptions for Modules + services.ConfigureOptions(); }); builder.Configure((app, routes, serviceProvider) => { - var fileProvider = serviceProvider.GetRequiredService(); - - var options = serviceProvider.GetRequiredService>().Value; - - options.RequestPath = ""; - options.FileProvider = fileProvider; - - var shellConfiguration = serviceProvider.GetRequiredService(); - - var cacheControl = shellConfiguration.GetValue("StaticFileOptions:CacheControl", $"public, max-age={TimeSpan.FromDays(30).TotalSeconds}, s-max-age={TimeSpan.FromDays(365.25).TotalSeconds}"); - - // Cache static files for a year as they are coming from embedded resources and should not vary - options.OnPrepareResponse = ctx => - { - ctx.Context.Response.Headers[HeaderNames.CacheControl] = cacheControl; - }; - - app.UseStaticFiles(options); + app.UseStaticFiles(); }); } diff --git a/src/docs/reference/core/Modules/README.md b/src/docs/reference/core/Modules/README.md index 497790cdf85..0054c090dec 100644 --- a/src/docs/reference/core/Modules/README.md +++ b/src/docs/reference/core/Modules/README.md @@ -60,9 +60,12 @@ You can find a sample application here: [`OrchardCore.Mvc.Web`](../../../../Orch The following configuration values are used by default and can be customized: +By default each tenant doesn't serve static files from Host `wwwroot`. To serve Host `wwwroot` for each tenant, set `ServeHostWebRoot` to `true` in tenant config. + ```json "StaticFileOptions": { // The CacheControl header sent with any static file served by modules - "CacheControl": "public, max-age=2592000, s-max-age=31557600" + "CacheControl": "public, max-age=2592000, s-max-age=31557600", + "ServeHostWebRoot" : false } ```