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
}
```