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所示的形式呈现在浏览器上。


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.NET Core应用针对静态文件请求的处理[4]: DirectoryBrowserMiddleware中间件如何呈现目录结构

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

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

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

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

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

  4. Laravel项目目录结构说明

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

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

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

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

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

  7. Vue学习(一)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. MVC 生成图片,下载文件

    /// <summary> /// 生成图片 /// </summary> /// <param name="collection"></ ...

  2. linux下安装jdk+tomcat+eclipse+mysql

    我的环境:主机是win7的,虚拟机是VWare  Workstation 6.0   ,linux系统为Red Hat Enterprise  Linux 5 64位 各软件版本:jdk是jdk-6u ...

  3. Java基础知识学习(九)

    GUI开发 先前用Java编写GUI程序,是使用抽象窗口工具包AWT(Abstract Window Toolkit).现在多用Swing.Swing可以看作是AWT的改良版,而不是代替AWT,是对A ...

  4. SQL Server字符串左匹配

    在SQL Server中经常会用到模糊匹配字符串的情况,最简单的办法就是使用like关键字(like语法http://msdn.microsoft.com/en-us/library/ms179859 ...

  5. maven 常见错误解决方法

    1. 最重要的一点,使用国内镜像,比如 oschina.net 的镜像: 搜 jar 推荐:http://maven.outofmemory.cn/,速度极快. 2. 错误:Could not res ...

  6. 010 使用netmap API接管网卡,接收数据包,回应ARP请求

    一.本文目的: 上一节中,我们已经在CentOS 6.7 上安装好了netmap,也能接收和发送包了,这节我们来调用netmap中的API,接管网卡,对网卡上收到的数据包做分析,并回应ARP请求. 二 ...

  7. System.getProperty()引起的悲剧--您的主机中的软件中止了一个已建立的连接

    我已无法形容此刻我的心情.. 本来是已经写好的netty5的demo程序,server和client之间创建tcp长连接的..然后随便传点数据的简单demo..然后今天试了一下tcp粘包的例子,用到了 ...

  8. 汽车4S店经验指标完成情况报表制作分享

    集团公司一般为了加强下属的经营管理,以及项经营指标完情况,需要制定一些报表.我们平时也经常遇到这种情况,而这些报表要包括什么内容呢?该怎么制作呢?用什么制作呢?今天小编就以4s店为例,分享给大家一个报 ...

  9. sobel算子原理及opencv源码实现

    sobel算子原理及opencv源码实现 简要描述 sobel算子主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测. 原理 算子使用两个33的矩阵(图1)算子使用两个33的矩阵(图1)去 ...

  10. stanford coursera 机器学习编程作业 exercise 3(使用神经网络 识别手写的阿拉伯数字(0-9))

    本作业使用神经网络(neural networks)识别手写的阿拉伯数字(0-9) 关于使用逻辑回归实现多分类问题:识别手写的阿拉伯数字(0-9),请参考:http://www.cnblogs.com ...