目录浏览允许在指定目录中列出目录里的文件及子目录。出于安全方面考虑默认情况下是关闭的可以通过UseDirectoryBrowser中间件开启指定目录浏览功能。通常情况下我们会这样使用
//启用默认目录浏览,即wwwroot app.UseDirectoryBrowser(); //或自定义指定目录浏览 var fileProvider = new PhysicalFileProvider($"{env.ContentRootPath}/MyImages"); app.UseDirectoryBrowser(new DirectoryBrowserOptions { RequestPath = "/MyImages", FileProvider = fileProvider });
开启之后当我们访问https://
/MyImages地址的时候将会展示如下效果,通过一个表格展示目录里的文件信息等
到中间件注册类[点击查看DirectoryBrowserExtensions源码]
public static class DirectoryBrowserExtensions { public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app) { return app.UseMiddleware<DirectoryBrowserMiddleware>(); } public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app, string requestPath) { return app.UseDirectoryBrowser(new DirectoryBrowserOptions { RequestPath = new PathString(requestPath) }); } public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app, DirectoryBrowserOptions options) { return app.UseMiddleware<DirectoryBrowserMiddleware>(Options.Create(options)); } }
这个中间件启用的重载方法和UseStaticFiles类似最终都是在传递DirectoryBrowserOptions,接下来我们就看DirectoryBrowserOptions传递了哪些信息[点击查看DirectoryBrowserOptions源码]
public class DirectoryBrowserOptions : SharedOptionsBase { public DirectoryBrowserOptions() : this(new SharedOptions()) { } public DirectoryBrowserOptions(SharedOptions sharedOptions) : base(sharedOptions) { } /// <summary> /// 目录格式化提供,默认是提供表格的形式展示,课自定义 /// </summary> public IDirectoryFormatter Formatter { get; set; } }
无独有偶这个类和StaticFileOptions一样也是集成自SharedOptionsBase类,唯一多了IDirectoryFormatter操作,通过它我们可以自定义展示到页面的输出形式,接下来我们就重点看下DirectoryBrowserMiddleware中间件的实现
public class DirectoryBrowserMiddleware { private readonly DirectoryBrowserOptions _options; private readonly PathString _matchUrl; private readonly RequestDelegate _next; private readonly IDirectoryFormatter _formatter; private readonly IFileProvider _fileProvider; public DirectoryBrowserMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv, IOptions<DirectoryBrowserOptions> options) : this(next, hostingEnv, HtmlEncoder.Default, options) { } public DirectoryBrowserMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv, HtmlEncoder encoder, IOptions<DirectoryBrowserOptions> options) { _next = next; _options = options.Value; //默认是提供默认目录的访问程序 _fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv); //默认传递的是HtmlDirectoryFormatter类型,也就是我们看到的输出表格的页面 _formatter = options.Value.Formatter ?? new HtmlDirectoryFormatter(encoder); _matchUrl = _options.RequestPath; } public Task Invoke(HttpContext context) { //1.IsGetOrHeadMethod判断是否为Get或Head请求 //2.TryMatchPath判断请求的路径和设置的路径是否可以匹配的上 //3.TryGetDirectoryInfo判断根据匹配出来的路径能否查找到真实的物理路径 if (context.GetEndpoint() == null && Helpers.IsGetOrHeadMethod(context.Request.Method) && Helpers.TryMatchPath(context, _matchUrl, forDirectory: true, subpath: out var subpath) && TryGetDirectoryInfo(subpath, out var contents)) { //判断请求路径是否是/为结尾 if (!Helpers.PathEndsInSlash(context.Request.Path)) { //如果不是以斜线结尾则重定向(个人感觉直接在服务端重定向就可以了,为啥还要返回浏览器在请求一次) context.Response.StatusCode = StatusCodes.Status301MovedPermanently; var request = context.Request; var redirect = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path + "https://www.jb51.net/", request.QueryString); context.Response.Headers[HeaderNames.Location] = redirect; return Task.CompletedTask; } //返回展示目录的内容 return _formatter.GenerateContentAsync(context, contents); } return _next(context); } /// <summary> /// 根据请求路径匹配到物理路径信息是否存在,存在则返回路径信息 /// </summary> private bool TryGetDirectoryInfo(PathString subpath, out IDirectoryContents contents) { contents = _fileProvider.GetDirectoryContents(subpath.Value); return contents.Exists; } }