DirectoryBrowserMiddleware中间件如何呈现目录结构

时间:2022-09-17 19:45:06

DirectoryBrowserMiddleware中间件如何呈现目录结构

和StaticFileMiddleware中间件一样,DirectoryBrowserMiddleware中间本质上还是定义了一个请求地址与某个物理目录之间的映射关系,而目标目录体现为一个FileProvider对象。当这个中间件接收到匹配的请求后,会根据请求地址解析出对应目录的相对路径,并利用这个FileProvider获取目录的内容。目录的内容最终会以一个HTML文档的形式被定义,而此HTML最终会被这个中间件作为响应的内容,“目录浏览器”的实现原理就这么简单。 [本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、DirectoryBrowserMiddleware
二、DirectoryFormatter
三、具体请求处理逻辑
四、自定义DirectoryFormatter

一、DirectoryBrowserMiddleware

接下来我们来看看DirectoryBrowserMiddleware的定义。如下面的代码片段所示,DirectoryBrowserMiddleware的第二个构造函数具有四个参数,其中第二个参数是代表当前执行环境的HostingEnvironment。作为第三个参数的是一个HtmlEncoder对象,当目标目录被呈现为一个HTML文档的时候,它被用于实现针对HTML的编码,如果没有显式指定(调用第一个构造函数),默认的HtmlEncoder(HtmlEncoder.Default)会被使用。至于第四个类型为IOptions<DirectoryBrowserOptions>的参数,则承载了针对DirectoryBrowserMiddleware的配置选项,DirectoryBrowserOptions与前面介绍的StaticFileOptions一样,它们都是SharedOptionsBase的子类。

   1: public class DirectoryBrowserMiddleware
   2: {
   3:     public DirectoryBrowserMiddleware(RequestDelegate next, IHostingEnvironment env, IOptions<DirectoryBrowserOptions> options)
   4:     public DirectoryBrowserMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, HtmlEncoder encoder, IOptions<DirectoryBrowserOptions> options);
   5:     public Task Invoke(HttpContext context);
   6: }
   7:  
   8: public class DirectoryBrowserOptions : SharedOptionsBase
   9: {
  10:     public IDirectoryFormatter Formatter { get; set; }
  11:  
  12:     public DirectoryBrowserOptions();
  13:     public DirectoryBrowserOptions(SharedOptions sharedOptions);
  14: }

二、DirectoryFormatter

DirectoryBrowserMiddleware中间件的目的很明确,就是将目录下的内容(文件和子目录)格式化成一种可读的形式响应给客户端,针对目录内容的响应最终实现在一个DirectoryFormatter对象上。DirectoryFormatter是我们对所有实现了IDirectoryFormatter接口的类型与对应对象的统称,DirectoryBrowserOptions的Formatter属性设置和返回的就是这个一个对象。

如下面的代码片段所示,IDirectoryFormatter接口仅仅包含一个GenerateContentAsync方法。当实现这个方法的时候,我们可以利用第一个类型为HttpContext的参数获取当前请求上下文的信息。该方法的另一个参数返回一组FileInfo的集合,每个FileInfo代表目标下的某个以文件或者子目录。

   1: public interface IDirectoryFormatter
   2: {
   3:     Task GenerateContentAsync(HttpContext context, IEnumerable<IFileInfo> contents);
   4: }

我们知道默认情况下请求目录的内容在页面上是以一个表格的形式被呈现的,包含这个表格的HTML文档是默认使用的DirectoryFormatter生成的,它是一个类型为HtmlDirectoryFormatter的对象。如下面的代码片段所示,我们在构造一个HtmlDirectoryFormatter对象的时候需要指定一个HtmlEncoder对象,该对象最初来源于构造DirectoryBrowserMiddleware时指定的那个HtmlEncoder对象。

   1: public class HtmlDirectoryFormatter : IDirectoryFormatter
   2: {
   3:     public HtmlDirectoryFormatter(HtmlEncoder encoder);
   4:     public virtual Task GenerateContentAsync(HttpContext context, IEnumerable<IFileInfo> contents);
   5: }

三、具体请求处理逻辑

既然最复杂的工作(呈现目录内容)都已经交给DirectoryFormatter来完成了,DirectoryBrowserMiddleware自身的工作其实就没有多少了。为了更好的说明这个中间件在处理请求是具体做了些什么,我们采用一种比较好理解的方式对DirectoryBrowserMiddleware类型进行了重新定义,具体的实现体现在如下所示的代码片段中。

   1: public class DirectoryBrowserMiddleware
   2: {
   3:     private RequestDelegate _next;
   4:     private DirectoryBrowserOptions _options;
   5:  
   6:     public DirectoryBrowserMiddleware(RequestDelegate next, IHostingEnvironment env, IOptions<DirectoryBrowserOptions> options) : this(next, env, HtmlEncoder.Default,options)
   7:     { }
   8:  
   9:     public DirectoryBrowserMiddleware(RequestDelegate next, IHostingEnvironment env, HtmlEncoder encoder, IOptions<DirectoryBrowserOptions> options)
  10:     {
  11:         _next                      = next;
  12:         _options                   = options.Value;
  13:         _options.FileProvider      = _options.FileProvider ?? env.WebRootFileProvider;
  14:         _options.Formatter         = _options.Formatter ?? new HtmlDirectoryFormatter(encoder);
  15:     }
  16:  
  17:     public async Task Invoke(HttpContext context)
  18:     {
  19:         //只处理GET和HEAD请求
  20:         if (!new string[] { "GET", "HEAD" }.Contains(context.Request.Method, StringComparer.OrdinalIgnoreCase))
  21:         {
  22:             await _next(context);
  23:             return;
  24:         }
  25:  
  26:        //检验当前路径是否与注册的请求路径相匹配
  27:         PathString path = new PathString(context.Request.Path.Value.TrimEnd('/') + "/");
  28:         PathString subpath;
  29:         if (!path.StartsWithSegments(_options.RequestPath, out subpath))
  30:         {
  31:             await _next(context); 
  32:             return;
  33:         }
  34:  
  35:         //检验目标目录是否存在
  36:         IDirectoryContents directoryContents = _options.FileProvider.GetDirectoryContents(subpath);
  37:         if (!directoryContents.Exists)
  38:         {
  39:             await _next(context); 
  40:             return;
  41:         }
  42:  
  43:         //如果当前路径不以"/"作为后缀,会响应一个针对“标准”URL的重定向
  44:         if (!context.Request.Path.Value.EndsWith("/"))
  45:         {
  46:             context.Response.StatusCode = 302;
  47:             context.Response.GetTypedHeaders().Location = new Uri(path.Value + context.Request.QueryString);
  48:             return;
  49:         }
  50:  
  51:         //利用DirectoryFormatter响应目录内容
  52:         await _options.Formatter.GenerateContentAsync(context, directoryContents);
  53:     }
  54: }

如上面的代码片段所示,当DirectoryBrowserMiddleware最终利用注册的DirectoryFormatter来响应目标目录的内容之前,它会做一系列的前期工作。比如它会验证当前请求是否是GET或者HEAD请求,以及当前的URL是否与注册的请求路径相匹配,在匹配的情况下还需要验证目标目录是否存在。除此之外,这个中间件要求访问目录的请求路劲必须以字符“/”作为后缀,否则会在目前的路径上添加这个后缀并针对最终的路径发送一个重定向。所以我们利用浏览器发送针对某个目录的请求的时候,URL明明没有指定“/”作为后缀,这个后缀会自动给我们加上,这就是重定向的作用。

四、自定义DirectoryFormatter

由于目录的内容在浏览器中的呈现方式完全由DirectoryFormatter完成,如果实现在HtmlDirectoryFormatter的默认呈现方式不能满足需求(比如我们需要这个页面与现有网站保持相同的风格),这可以通过注册一个自定义的DirectoryFormatter来完成。接下来我们通过一个简单的实例来演示如何定义这么一个DirectoryFormatter。我们将自定义的DirectoryFormatter命名为ListDirectoryFormatter,应为它仅仅将所有文件或者子目录显示为一个简单的列表。

   1: public class ListDirectoryFormatter : IDirectoryFormatter
   2: {
   3:     public async Task GenerateContentAsync(HttpContext context, IEnumerable<IFileInfo> contents)
   4:     {
   5:         context.Response.ContentType = "text/html";
   6:         await context.Response.WriteAsync("<html><head><title>Index</title><body><ul>");
   7:         foreach (var file in contents)
   8:         {
   9:             string href = $"{context.Request.Path.Value.TrimEnd('/')}/{file.Name}";
  10:             await context.Response.WriteAsync($"<li><a href='{href}'>{file.Name}</a></li>");
  11:         }
  12:         await context.Response.WriteAsync("</ul></body></html>");
  13:     }
  14: }
  15:  
  16: public class Program
  17: {
  18:     public static void Main()
  19:     {
  20:         new WebHostBuilder()
  21:             .UseContentRoot(Directory.GetCurrentDirectory())
  22:             .UseKestrel()
  23:             .Configure(app => app.UseDirectoryBrowser(new DirectoryBrowserOptions {Formatter = new ListDirectoryFormatter()}))
  24:             .Build()
  25:             .Run();
  26:     }
  27: }

如上面的代码片段,ListDirectoryFormatter最终响应的是一个完整的HTML文档,它的主体部分只包含一个通过<ul>…</ul>表示的无序列表。列表元素(<li>)是一个针对文件或者子目录的链接。在调用扩展方法UseDirectoryBrowser注册DirectoryBrowserMiddleware中间件的时候,我们为将一个ListDirectoryFormatter对象设置为DirectoryBrowserOptions的Formatter属性。目录内容最终将会采用如图9所示的形式呈现在浏览器上。

DirectoryBrowserMiddleware中间件如何呈现目录结构


ASP.NET Core应用针对静态文件请求的处理[1]: 以Web的形式发布静态文件
ASP.NET Core应用针对静态文件请求的处理[2]: 条件请求与区间请求
ASP.NET Core应用针对静态文件请求的处理[3]: StaticFileMiddleware中间件如何处理针对文件请求
ASP.NET Core应用针对静态文件请求的处理[4]: DirectoryBrowserMiddleware中间件如何呈现目录结构
ASP.NET Core应用针对静态文件请求的处理[5]: DefaultFilesMiddleware中间件如何显示默认页面

作者:蒋金楠 
微信公众账号:大内老A
微博:www.weibo.com/artech

DirectoryBrowserMiddleware中间件如何呈现目录结构的更多相关文章

  1. ASP&period;NET Core应用针对静态文件请求的处理&lbrack;4&rsqb;&colon; DirectoryBrowserMiddleware中间件如何呈现目录结构

    和StaticFileMiddleware中间件一样,DirectoryBrowserMiddleware中间本质上还是定义了一个请求地址与某个物理目录之间的映射关系,而目标目录体现为一个FilePr ...

  2. react第十八单元&lpar;redux中间件redux-thunk&comma;redux工程目录的样板代码,规范目录结构&rpar;

    第十八单元(redux中间件redux-thunk,redux工程目录的样板代码,规范目录结构) #课程目标 中间件:中间件增强redux的可扩展性,实现功能复用的目的. redux-thunk异步逻 ...

  3. DeveloperExceptionPageMiddleware中间件如何呈现&OpenCurlyDoubleQuote;开发者异常页面”

    DeveloperExceptionPageMiddleware中间件如何呈现"开发者异常页面" 在<ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式&g ...

  4. Laravel项目目录结构说明

    Laravel项目目录结构说明: |- vendor 目录包含你的 Composer 依赖模块及laravel框架. |- bootstrap 目录包含几个框架启动跟自动加载配置的文件. |- app ...

  5. Hadoop阅读笔记(五)——重返Hadoop目录结构

    常言道:男人是视觉动物.我觉得不完全对,我的理解是范围再扩大点,不管男人女人都是视觉动物.某些场合(比如面试.初次见面等),别人没有那么多的闲暇时间听你诉说过往以塑立一个关于你的完整模型.所以,第一眼 ...

  6. TP学习笔记一(tp的目录结构 &comma; tp的输出方式)

    一.ThinkPHP的介绍 //了解 MVC M - Model 模型 工作:负责数据的操作 V - View 视图(模板) 工作:负责前台页面显示 C - Controller 控制器(模块) 工作 ...

  7. Vue学习&lpar;一&rpar;Vue目录结构

    安装教程网上一大把,可以自己搜索.记录下学习过程. 认识下Vue的目录结构,取自:https://www.cnblogs.com/dragonir/p/8711761.html vue 文件目录结构详 ...

  8. Linux 目录结构详解

    Linux目录详解 Linux目录详解(RHEL5.4) 由于linux是开放源代码,各大公司和团体根据linux的核心代码做各自的操作,编程.这样就造成在根下的目录的不同.这样就造成个人不能使用他人 ...

  9. 比起Windows,怎样解读Linux的文件系统与目录结构?

    比起Windows,怎样解读Linux的文件系统与目录结构? Linux 和Windows的文件系统有些不同,在学习使用 Linux 之前,若能够了解这些不同,会有助于后续学习. 本文先对Window ...

随机推荐

  1. korn shell

    "$*" is a single string that consists of all of the positional parameters "$@"   ...

  2. 元素水平垂直居中(transform,margin,table-cell,jQuery)

    1.水平居中 .div{ margin:0 auto; (或者 margin:auto;) width:500px; height:300px; } 2.使用margin水平垂直居中 方式一: .di ...

  3. 【Spring源码分析】&period;properties文件读取及占位符&dollar;&lbrace;&period;&period;&period;&rcub;替换源码解析

    前言 我们在开发中常遇到一种场景,Bean里面有一些参数是比较固定的,这种时候通常会采用配置的方式,将这些参数配置在.properties文件中,然后在Bean实例化的时候通过Spring将这些.pr ...

  4. html5 Canvas绘制时钟以及绘制运动的圆

    1.绘制时钟 <!-- js代码 --> <script type="text/javascript"> window.onload=function(){ ...

  5. WOW&period;js和animate&period;css让页面滚动时显示动画

    官网:http://mynameismatthieu.com/WOW/ bootstrap CDN服务:http://www.bootcdn.cn/wow/ 1.wow.js 实现了在网页滚动时的动画 ...

  6. 01python初识—编辑器&amp&semi;版本&amp&semi;变量知识

    python2.0和3.0版本变化很大,要跟随脚步,学新的,用新的.3.0 python开发工具pycharm 5.0 python的交互器 python的程序一般放到Linux环境下运行. pyth ...

  7. java如何实现以数据流的形式下载压缩包到本地&quest;

    先不多说,直接贴代码吧,在服务器的E盘下放一个E:/manual.rar的压缩包 package com.cellstrain.icell.controller; import org.springf ...

  8. php如何定时执行任务

    PHP的实现决定了它没有Java和.Net这种AppServer的概念, 而http协议是一个无状态的协议, php只能被用户触发, 被调用, 调用后会自动退出内存, 没有常驻内存, 就没有办法准确的 ...

  9. EasyMvc入门教程-基本控件说明(2)定时器

    我们有时候希望系统能自动刷新后台数据或者做某个动作,那么定时器的作用就体现出来了. EasyMvc默认提供的服务器可以定时调前端方法与定时调后端方法,先看例子: 定时调客户端事件 实现代码: @Htm ...

  10. 爬虫之scapy

    一 介绍 Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速.简单.可扩展的方式从网站中提取所需的数据.但目前Scrapy的用途十分广泛,可 ...