一、中间件的定义
中间件就是嵌入到请求处理管道中用于处理请求和响应的一段代码。它主要有两个作用:
- 处理请求和响应
- 决定请求是否传递到下一个组件
二、中间件原理(RequestDelegate)
中间件是请求处理管道的一部分,所以中间件的主要作用就是处理当前请求。当前请求的所有信息都被封装到HttpContext对象中,可以由该对象来控制http的请求和响应。因此,我们就可以把中间件抽象为一种操作,这个操作接收一个HttpContext对象,并对该对象进行一些处理。可以把这个操作定义为一个函数:
public void Middleware(HttpContext context);
每个中间件处理函数的名字可能不尽相同,但是有一点共同的就是他们都接受一个HttpContext对象。而在C#中,委托是对函数的抽象。因此我们将上面函数抽象为一个委托:
public delegate void Middleware(HttpContext context);
RequestDelegate就是这样一个委托,唯一不同的是,他返回的是一个Task类型:
public delegate Task RequestDelegate(HttpContext context);
实际上,在netcore中,组成请求处理管道的中间件可以表示为一个类型为Func<RequestDelegate, RequestDelegate>的泛型委托对象。
public Func<RequestDelegate, RequestDelegate> Middleware;
为什么中间件不是RequestDelegate委托对象呢?中间件并不孤立地存在,所有注册的中间件最终会根据注册的先后顺序组成一个链表,每个中间件不仅仅需要完成各自的请求处理任务外,还需要驱动链表中的下一个中间件。
对于一个由多个Func<RequestDelegate, RequestDelegate>对象组成的中间件链表来说,某个中间件会将后一个Func<RequestDelegate, RequestDelegate>对象的返回值作为输入,而自身的返回值则作为前一个中间件的输入。某个中间件执行之后返回的RequestDelegate对象不仅仅体现了自身对请求的处理操作,而是体现了包含自己和后续中间件对请求的处理。那么对于第一个中间件来说,它执行后返回的RequestDelegate对象实际上体现了整个应用对请求的处理逻辑。
三、中间件管道(IApplicationBuilder)
中间件管道即中间件委托链。IApplicationBuilder是管道的注册者和构建者。以类型为Func<RequestDelegate, RequestDelegate>的委托对象表示的中间件需要在启动的时候注册到应用程序上,所有注册的中间件最终会转换成一个代表中间件委托链的RequestDelegate对象,它们按照注册顺序对请求的处理流程最终体现在对这个委托对象的执行。不论是最终将中间件转换成RequestDelegate对象,还是最初对它们的注册,都是通过一个ApplicationBuilder对象来完成的。
namespace Microsoft.AspNetCore.Builder { // // 摘要: // Defines a class that provides the mechanisms to configure an application's request // pipeline. public interface IApplicationBuilder { // // 摘要: // Gets or sets the System.IServiceProvider that provides access to the application's // service container. IServiceProvider ApplicationServices { get; set; } // // 摘要: // Gets the set of HTTP features the application's server provides. IFeatureCollection ServerFeatures { get; } // // 摘要: // Gets a key/value collection that can be used to share data between middleware. IDictionary<string, object?> Properties { get; } // // 摘要: // Builds the delegate used by this application to process HTTP requests. // // 返回结果: // The request handling delegate. RequestDelegate Build(); // // 摘要: // Creates a new Microsoft.AspNetCore.Builder.IApplicationBuilder that shares the // Microsoft.AspNetCore.Builder.IApplicationBuilder.Properties of this Microsoft.AspNetCore.Builder.IApplicationBuilder. // // 返回结果: // The new Microsoft.AspNetCore.Builder.IApplicationBuilder. IApplicationBuilder New(); // // 摘要: // Adds a middleware delegate to the application's request pipeline. // // 参数: // middleware: // The middleware delegate. // // 返回结果: // The Microsoft.AspNetCore.Builder.IApplicationBuilder. IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); } }
-
Use:用于中间件的注册
-
Build:用于将注册的中间件列表生成代表中间件委托链的RequestDelegate对象
-
New:根据自己“克隆”出一个新的ApplicationBuilder对象,这两个ApplicationBuilder对象应用具有相同的属性集合
-
ApplicationServices属性:代表针对当前应用程序的依赖注入容器
-
ServerFeatures属性:返回服务器提供的特性集合
-
Properties属性:返回的字典代表一个可以用来存放任意属性的容器
ApplicationBuilder是我们对所有实现了IApplicationBuilder接口的所有类型以及对应对象的统称。ApplicationBuilder类型利用一个List<Func<RequestDelegate, RequestDelegate>>对象来保存注册的中间件,所以Use方法只需要将指定的中间件添加到这个列表中即可,而Build方法只需要逆序调用这些注册的中间件对应的Func<RequestDelegate, RequestDelegate>对象就能得到我们需要的RequestDelegate对象。值得一提的是,Build方法实际上在中间件链条的尾部添加了一个额外的中间件,该中间件会负责将响应状态码设置为404,如果我们没有注册一个中间件对请求作最终的响应,整个管道比较回复一个状态码为404的响应。
public class ApplicationBuilder : IApplicationBuilder { private readonly IList<Func<RequestDelegate, RequestDelegate>> middlewares = new List<Func<RequestDelegate, RequestDelegate>>(); public IDictionary<string, object> Properties { get; } public IServiceProvider ApplicationServices { get { return GetProperty<IServiceProvider>("application.Services"); } set { SetProperty<IServiceProvider>("application.Services", value); } } public IFeatureCollection ServerFeatures { get { return GetProperty<IFeatureCollection>("server.Features"); } } public ApplicationBuilder(IServiceProvider serviceProvider) { this.Properties = new Dictionary<string, object>(); ApplicationServices = serviceProvider; } public ApplicationBuilder(IServiceProvider serviceProvider, object server) : this(serviceProvider) { SetProperty("server.Features", server); } public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { middlewares.Add(middleware); return this; } public IApplicationBuilder New() { return new ApplicationBuilder(this); } public RequestDelegate Build() { RequestDelegate app = context => { context.Response.StatusCode = 404; return Task.FromResult(0); }; foreach (var component in middlewares.Reverse()) { app = component(app); } return app; } private ApplicationBuilder(ApplicationBuilder builder) =>Properties = new CopyOnWriteDictionary<string, object>(builder.Properties, StringComparer.Ordinal); private T GetProperty<T>(string key)=>Properties.TryGetValue(key, out var value) ? (T)value : default; private void SetProperty<T>(string key, T value)=> Properties[key] = value; }