| File: StaticFileMiddleware.cs | Web Access |
| Project: src\aspnetcore\src\Middleware\StaticFiles\src\Microsoft.AspNetCore.StaticFiles.csproj (Microsoft.AspNetCore.StaticFiles) |
// 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.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.StaticFiles; /// <summary> /// Enables serving static files for a given request path /// </summary> public class StaticFileMiddleware { private readonly StaticFileOptions _options; private readonly PathString _matchUrl; private readonly RequestDelegate _next; private readonly ILogger _logger; private readonly IFileProvider _fileProvider; private readonly IContentTypeProvider _contentTypeProvider; /// <summary> /// Creates a new instance of the StaticFileMiddleware. /// </summary> /// <param name="next">The next middleware in the pipeline.</param> /// <param name="hostingEnv">The <see cref="IWebHostEnvironment"/> used by this middleware.</param> /// <param name="options">The configuration options.</param> /// <param name="loggerFactory">An <see cref="ILoggerFactory"/> instance used to create loggers.</param> public StaticFileMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv, IOptions<StaticFileOptions> options, ILoggerFactory loggerFactory) { ArgumentNullException.ThrowIfNull(next); ArgumentNullException.ThrowIfNull(hostingEnv); ArgumentNullException.ThrowIfNull(options); ArgumentNullException.ThrowIfNull(loggerFactory); _next = next; _options = options.Value; _contentTypeProvider = _options.ContentTypeProvider ?? new FileExtensionContentTypeProvider(); _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> /// Processes a request to determine if it matches a known file, and if so, serves it. /// </summary> /// <param name="context"></param> /// <returns></returns> public Task Invoke(HttpContext context) { if (!ValidateNoEndpointDelegate(context)) { _logger.EndpointMatched(); } else if (!ValidateMethod(context)) { _logger.RequestMethodNotSupported(context.Request.Method); } else if (!ValidatePath(context, _matchUrl, out var subPath)) { _logger.PathMismatch(subPath); } else if (!LookupContentType(_contentTypeProvider, _options, subPath, out var contentType)) { _logger.FileTypeNotSupported(subPath); } else { // If we get here, we can try to serve the file return TryServeStaticFile(context, contentType, subPath); } return _next(context); } // Return true because we only want to run if there is no endpoint delegate. private static bool ValidateNoEndpointDelegate(HttpContext context) => context.GetEndpoint()?.RequestDelegate is null; private static bool ValidateMethod(HttpContext context) { return Helpers.IsGetOrHeadMethod(context.Request.Method); } internal static bool ValidatePath(HttpContext context, PathString matchUrl, out PathString subPath) => Helpers.TryMatchPath(context, matchUrl, forDirectory: false, out subPath); internal static bool LookupContentType(IContentTypeProvider contentTypeProvider, StaticFileOptions options, PathString subPath, out string? contentType) { if (contentTypeProvider.TryGetContentType(subPath.Value!, out contentType)) { return true; } if (options.ServeUnknownFileTypes) { contentType = options.DefaultContentType; return true; } return false; } private Task TryServeStaticFile(HttpContext context, string? contentType, PathString subPath) { var fileContext = new StaticFileContext(context, _options, _logger, _fileProvider, contentType, subPath); if (!fileContext.LookupFileInfo()) { _logger.FileNotFound(fileContext.SubPath); } else { // If we get here, we can try to serve the file return fileContext.ServeStaticFile(context, _next); } return _next(context); } }