diff --git a/src/Hosting/Abstractions/src/IWebHostEnvironment.cs b/src/Hosting/Abstractions/src/IWebHostEnvironment.cs
index 13aca8415030..167e0d10857d 100644
--- a/src/Hosting/Abstractions/src/IWebHostEnvironment.cs
+++ b/src/Hosting/Abstractions/src/IWebHostEnvironment.cs
@@ -13,11 +13,13 @@ public interface IWebHostEnvironment : IHostEnvironment
{
///
/// Gets or sets the absolute path to the directory that contains the web-servable application content files.
+ /// This defaults to the 'wwwroot' subfolder.
///
string WebRootPath { get; set; }
///
/// Gets or sets an pointing at .
+ /// This defaults to referencing files from the 'wwwroot' subfolder.
///
IFileProvider WebRootFileProvider { get; set; }
}
diff --git a/src/Middleware/Middleware.slnf b/src/Middleware/Middleware.slnf
index de0c7112205f..abfa304b9ed1 100644
--- a/src/Middleware/Middleware.slnf
+++ b/src/Middleware/Middleware.slnf
@@ -114,6 +114,7 @@
"src\\Middleware\\WebSockets\\test\\UnitTests\\Microsoft.AspNetCore.WebSockets.Tests.csproj",
"src\\Middleware\\perf\\Microbenchmarks\\Microsoft.AspNetCore.WebSockets.Microbenchmarks.csproj",
"src\\Middleware\\perf\\ResponseCaching.Microbenchmarks\\Microsoft.AspNetCore.ResponseCaching.Microbenchmarks.csproj",
+ "src\\ObjectPool\\src\\Microsoft.Extensions.ObjectPool.csproj",
"src\\Security\\Authorization\\Core\\src\\Microsoft.AspNetCore.Authorization.csproj",
"src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj",
"src\\Servers\\HttpSys\\src\\Microsoft.AspNetCore.Server.HttpSys.csproj",
@@ -122,7 +123,9 @@
"src\\Servers\\IIS\\IntegrationTesting.IIS\\src\\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj",
"src\\Servers\\Kestrel\\Core\\src\\Microsoft.AspNetCore.Server.Kestrel.Core.csproj",
"src\\Servers\\Kestrel\\Kestrel\\src\\Microsoft.AspNetCore.Server.Kestrel.csproj",
- "src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj"
+ "src\\Servers\\Kestrel\\Transport.Quic\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.csproj",
+ "src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj",
+ "src\\WebEncoders\\src\\Microsoft.Extensions.WebEncoders.csproj"
]
}
}
\ No newline at end of file
diff --git a/src/Middleware/StaticFiles/src/DefaultFilesExtensions.cs b/src/Middleware/StaticFiles/src/DefaultFilesExtensions.cs
index b9f12c694005..21b51e265738 100644
--- a/src/Middleware/StaticFiles/src/DefaultFilesExtensions.cs
+++ b/src/Middleware/StaticFiles/src/DefaultFilesExtensions.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Options;
@@ -17,6 +18,10 @@ public static class DefaultFilesExtensions
///
///
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app)
{
if (app == null)
@@ -33,6 +38,10 @@ public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app)
///
/// The relative request path.
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app, string requestPath)
{
if (app == null)
diff --git a/src/Middleware/StaticFiles/src/DirectoryBrowserExtensions.cs b/src/Middleware/StaticFiles/src/DirectoryBrowserExtensions.cs
index 4e82f0c5f0c3..1c91f0d08f2d 100644
--- a/src/Middleware/StaticFiles/src/DirectoryBrowserExtensions.cs
+++ b/src/Middleware/StaticFiles/src/DirectoryBrowserExtensions.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Options;
@@ -17,6 +18,10 @@ public static class DirectoryBrowserExtensions
///
///
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app)
{
if (app == null)
@@ -33,6 +38,10 @@ public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder a
///
/// The relative request path.
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app, string requestPath)
{
if (app == null)
diff --git a/src/Middleware/StaticFiles/src/FileServerExtensions.cs b/src/Middleware/StaticFiles/src/FileServerExtensions.cs
index 3b029251090c..42dc3c1a6cab 100644
--- a/src/Middleware/StaticFiles/src/FileServerExtensions.cs
+++ b/src/Middleware/StaticFiles/src/FileServerExtensions.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Builder;
@@ -16,6 +17,10 @@ public static class FileServerExtensions
///
///
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseFileServer(this IApplicationBuilder app)
{
if (app == null)
@@ -32,6 +37,10 @@ public static IApplicationBuilder UseFileServer(this IApplicationBuilder app)
///
/// Should directory browsing be enabled?
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseFileServer(this IApplicationBuilder app, bool enableDirectoryBrowsing)
{
if (app == null)
@@ -51,6 +60,10 @@ public static IApplicationBuilder UseFileServer(this IApplicationBuilder app, bo
///
/// The relative request path.
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseFileServer(this IApplicationBuilder app, string requestPath)
{
if (app == null)
diff --git a/src/Middleware/StaticFiles/src/Infrastructure/SharedOptionsBase.cs b/src/Middleware/StaticFiles/src/Infrastructure/SharedOptionsBase.cs
index 51ca710e9e5d..bc01b8646707 100644
--- a/src/Middleware/StaticFiles/src/Infrastructure/SharedOptionsBase.cs
+++ b/src/Middleware/StaticFiles/src/Infrastructure/SharedOptionsBase.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.FileProviders;
@@ -32,6 +33,7 @@ protected SharedOptionsBase(SharedOptions sharedOptions)
///
/// The relative request path that maps to static resources.
+ /// This defaults to the site root '/'.
///
public PathString RequestPath
{
@@ -42,6 +44,10 @@ public PathString RequestPath
///
/// The file system used to locate resources
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public IFileProvider? FileProvider
{
get { return SharedOptions.FileProvider; }
diff --git a/src/Middleware/StaticFiles/src/LoggerExtensions.cs b/src/Middleware/StaticFiles/src/LoggerExtensions.cs
index 8902c42af781..ade489f98ba9 100644
--- a/src/Middleware/StaticFiles/src/LoggerExtensions.cs
+++ b/src/Middleware/StaticFiles/src/LoggerExtensions.cs
@@ -58,4 +58,8 @@ public static void FileServed(this ILogger logger, string virtualPath, string ph
[LoggerMessage(14, LogLevel.Debug, "The file transmission was cancelled", EventName = "WriteCancelled")]
public static partial void WriteCancelled(this ILogger logger, Exception ex);
+
+ [LoggerMessage(16, LogLevel.Warning,
+ "The WebRootPath was not found: {WebRootPath}. Static files may be unavailable.", EventName = "WebRootPathNotFound")]
+ public static partial void WebRootPathNotFound(this ILogger logger, string webRootPath);
}
diff --git a/src/Middleware/StaticFiles/src/StaticFileExtensions.cs b/src/Middleware/StaticFiles/src/StaticFileExtensions.cs
index 9466c86ce96f..5fec481dfc1d 100644
--- a/src/Middleware/StaticFiles/src/StaticFileExtensions.cs
+++ b/src/Middleware/StaticFiles/src/StaticFileExtensions.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Options;
@@ -17,6 +18,10 @@ public static class StaticFileExtensions
///
///
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app)
{
if (app == null)
@@ -33,6 +38,10 @@ public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app)
///
/// The relative request path.
///
+ ///
+ /// Files are served from the path specified in
+ /// or which defaults to the 'wwwroot' subfolder.
+ ///
public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app, string requestPath)
{
if (app == null)
diff --git a/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs b/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs
index aa3e44193f21..446024ffe732 100644
--- a/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs
+++ b/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs
@@ -57,6 +57,12 @@ public StaticFileMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv
_fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv);
_matchUrl = _options.RequestPath;
_logger = loggerFactory.CreateLogger();
+
+ // See HostingEnvironmentExtensions.Initialize
+ if (_fileProvider is NullFileProvider && _fileProvider == hostingEnv.WebRootFileProvider)
+ {
+ _logger.WebRootPathNotFound(Path.GetFullPath(Path.Combine(hostingEnv.ContentRootPath, hostingEnv.WebRootPath ?? "wwwroot")));
+ }
}
///
diff --git a/src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs b/src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs
index bdb9347a8690..0fdfce5424c1 100644
--- a/src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs
+++ b/src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs
@@ -17,12 +17,13 @@
namespace Microsoft.AspNetCore.StaticFiles;
-public class StaticFileMiddlewareTests
+public class StaticFileMiddlewareTests : LoggedTest
{
[Fact]
public async Task ReturnsNotFoundWithoutWwwroot()
{
using var host = new HostBuilder()
+ .ConfigureServices(AddTestLogging)
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
@@ -38,6 +39,9 @@ public async Task ReturnsNotFoundWithoutWwwroot()
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
Assert.Null(response.Headers.ETag);
+
+ Assert.Contains(TestSink.Writes, w => w.Message.Contains("The WebRootPath was not found")
+ && w.Message.Contains("Static files may be unavailable."));
}
[ConditionalFact]
@@ -52,6 +56,7 @@ public async Task ReturnsNotFoundForBrokenSymlink()
try
{
using var host = new HostBuilder()
+ .ConfigureServices(AddTestLogging)
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
@@ -83,6 +88,7 @@ public async Task ReturnsNotFoundIfSendFileThrows()
.ThrowsAsync(new FileNotFoundException());
mockSendFile.Setup(m => m.Stream).Returns(Stream.Null);
using var host = new HostBuilder()
+ .ConfigureServices(AddTestLogging)
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder