Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Hosting/Abstractions/src/IWebHostEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ public interface IWebHostEnvironment : IHostEnvironment
{
/// <summary>
/// Gets or sets the absolute path to the directory that contains the web-servable application content files.
/// This defaults to the 'wwwroot' subfolder.
/// </summary>
string WebRootPath { get; set; }

/// <summary>
/// Gets or sets an <see cref="IFileProvider"/> pointing at <see cref="WebRootPath"/>.
/// This defaults to referencing files from the 'wwwroot' subfolder.
/// </summary>
IFileProvider WebRootFileProvider { get; set; }
}
5 changes: 4 additions & 1 deletion src/Middleware/Middleware.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
]
}
}
9 changes: 9 additions & 0 deletions src/Middleware/StaticFiles/src/DefaultFilesExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,6 +18,10 @@ public static class DefaultFilesExtensions
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app)
{
if (app == null)
Expand All @@ -33,6 +38,10 @@ public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app)
/// <param name="app"></param>
/// <param name="requestPath">The relative request path.</param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app, string requestPath)
{
if (app == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,6 +18,10 @@ public static class DirectoryBrowserExtensions
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app)
{
if (app == null)
Expand All @@ -33,6 +38,10 @@ public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder a
/// <param name="app"></param>
/// <param name="requestPath">The relative request path.</param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app, string requestPath)
{
if (app == null)
Expand Down
13 changes: 13 additions & 0 deletions src/Middleware/StaticFiles/src/FileServerExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,6 +17,10 @@ public static class FileServerExtensions
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseFileServer(this IApplicationBuilder app)
{
if (app == null)
Expand All @@ -32,6 +37,10 @@ public static IApplicationBuilder UseFileServer(this IApplicationBuilder app)
/// <param name="app"></param>
/// <param name="enableDirectoryBrowsing">Should directory browsing be enabled?</param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseFileServer(this IApplicationBuilder app, bool enableDirectoryBrowsing)
{
if (app == null)
Expand All @@ -51,6 +60,10 @@ public static IApplicationBuilder UseFileServer(this IApplicationBuilder app, bo
/// <param name="app"></param>
/// <param name="requestPath">The relative request path.</param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseFileServer(this IApplicationBuilder app, string requestPath)
{
if (app == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -32,6 +33,7 @@ protected SharedOptionsBase(SharedOptions sharedOptions)

/// <summary>
/// The relative request path that maps to static resources.
/// This defaults to the site root '/'.
/// </summary>
public PathString RequestPath
{
Expand All @@ -42,6 +44,10 @@ public PathString RequestPath
/// <summary>
/// The file system used to locate resources
/// </summary>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public IFileProvider? FileProvider
{
get { return SharedOptions.FileProvider; }
Expand Down
4 changes: 4 additions & 0 deletions src/Middleware/StaticFiles/src/LoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
9 changes: 9 additions & 0 deletions src/Middleware/StaticFiles/src/StaticFileExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,6 +18,10 @@ public static class StaticFileExtensions
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app)
{
if (app == null)
Expand All @@ -33,6 +38,10 @@ public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app)
/// <param name="app"></param>
/// <param name="requestPath">The relative request path.</param>
/// <returns></returns>
/// <remarks>
/// Files are served from the path specified in <see cref="IWebHostEnvironment.WebRootPath"/>
/// or <see cref="IWebHostEnvironment.WebRootFileProvider"/> which defaults to the 'wwwroot' subfolder.
/// </remarks>
public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app, string requestPath)
{
if (app == null)
Expand Down
6 changes: 6 additions & 0 deletions src/Middleware/StaticFiles/src/StaticFileMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ public StaticFileMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv
_fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv);
_matchUrl = _options.RequestPath;
_logger = loggerFactory.CreateLogger<StaticFileMiddleware>();

// See HostingEnvironmentExtensions.Initialize
if (_fileProvider is NullFileProvider && _fileProvider == hostingEnv.WebRootFileProvider)
{
_logger.WebRootPathNotFound(Path.GetFullPath(Path.Combine(hostingEnv.ContentRootPath, hostingEnv.WebRootPath ?? "wwwroot")));
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]
Expand All @@ -52,6 +56,7 @@ public async Task ReturnsNotFoundForBrokenSymlink()
try
{
using var host = new HostBuilder()
.ConfigureServices(AddTestLogging)
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
Expand Down Expand Up @@ -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
Expand Down