// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.StaticFiles { /// /// Enables directory browsing /// public class DirectoryBrowserMiddleware { private readonly DirectoryBrowserOptions _options; private readonly PathString _matchUrl; private readonly RequestDelegate _next; private readonly IDirectoryFormatter _formatter; private readonly IFileProvider _fileProvider; /// /// Creates a new instance of the SendFileMiddleware. Using instance. /// /// The next middleware in the pipeline. /// The used by this middleware. /// The configuration for this middleware. public DirectoryBrowserMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, IOptions options) : this(next, hostingEnv, HtmlEncoder.Default, options) { } /// /// Creates a new instance of the SendFileMiddleware. /// /// The next middleware in the pipeline. /// The used by this middleware. /// The used by the default . /// The configuration for this middleware. public DirectoryBrowserMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, HtmlEncoder encoder, IOptions options) { if (next == null) { throw new ArgumentNullException(nameof(next)); } if (hostingEnv == null) { throw new ArgumentNullException(nameof(hostingEnv)); } if (encoder == null) { throw new ArgumentNullException(nameof(encoder)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } _next = next; _options = options.Value; _fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv); _formatter = options.Value.Formatter ?? new HtmlDirectoryFormatter(encoder); _matchUrl = _options.RequestPath; } /// /// Examines the request to see if it matches a configured directory. If so, a view of the directory contents is returned. /// /// /// public Task Invoke(HttpContext context) { // Check if the URL matches any expected paths PathString subpath; IDirectoryContents contents; if (Helpers.IsGetOrHeadMethod(context.Request.Method) && Helpers.TryMatchPath(context, _matchUrl, forDirectory: true, subpath: out subpath) && TryGetDirectoryInfo(subpath, out contents)) { // If the path matches a directory but does not end in a slash, redirect to add the slash. // This prevents relative links from breaking. if (!Helpers.PathEndsInSlash(context.Request.Path)) { context.Response.StatusCode = 301; context.Response.Headers[HeaderNames.Location] = context.Request.PathBase + context.Request.Path + "/" + context.Request.QueryString; return Constants.CompletedTask; } return _formatter.GenerateContentAsync(context, contents); } return _next(context); } private bool TryGetDirectoryInfo(PathString subpath, out IDirectoryContents contents) { contents = _fileProvider.GetDirectoryContents(subpath.Value); return contents.Exists; } } }