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