AspNetCore 中间件 ---- 委托
什么是中间件
Asp.Net中间件指的是一个独立的应用程序,它是asp.net应用程序处理Http请求管道的一个组成部分。中间件可以对游走在其中的Http请求或者响应做出修改,例如一个身份验证的中间件就可以从Http请求中读取信息并和数据库中的信息做匹配,根据匹配结果显示对应的http的响应结果。
RequestDelegate
asp.net中间件是http处理管道的一部分,所以中间件的主要作用就是处理当前http请求。asp.net中,当前http请求的所有信息都被封装到HttpContext对象中,可以由该对象来控制http请求的请求和响应。这样,我们就可以把中间件抽象为一种操作,这个操作接收一个HttpContext对象,并对他进行一些处理。所以很容易我们就能想到把这个操作定义为一个函数:
public void Middleware(HttpContext context);
每个中间件处理函数的名字可能不尽相同,但是有一点共同的就是他们都接受一个HttpContext对象。在OOP中,类是对对象的抽象,而在C#中,委托则是对函数的抽象。这样我们就容易的将上面这个函数抽象为一个委托:
public delegate void Middleware(HttpContext context);
RequestDelegate就是这样一个委托,唯一不同的是,他返回的是一个Task类型:
public delegate Task RequestDelegate(HttpContext context);
但是AspNetCore中的中间件定义是:
public Func<RequestDelegate, RequestDelegate> Middleware;
Func<RequestDelegate, RequestDelegate>
为什么不是RequestDelegate?因为aspnetcore中,管道就是中间件串联成的一条链,Http请求在通过这个管道时,会有不同的中间件对他处理,并把处理结果返回给下一个中间件。所以我们很容易想到Func第一个参数是上一个中间件的处理结果,第二个就是当前中间件的处理结果。但是~~~~,事情并没有这么简单,第一个RequestDelegate代表的是下一个中间件。
为什么第一个RequestDelegate是下一个中间件呢?就如上面所说,aspnet core的http请求处理管道就是由中间件串联的管道,所以对于管道中下一个中间件的调用,就交给上一个中间件负责。如果上一个中间件决定不调用下一个中间件,那整个管道末端就是上一个中间件,也就是所谓的短路(short-circuiting)。
下面来看一下具体的代码实现。
ApplicationBuilder
首先对于ApplicationBuilder对象来说,它有一个存储中间件的列表对象_components:
private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
当我们每次调用 app.Use(Func<RequestDelegate, RequestDelegate> middleware) 方法时,就会将中间件添加到这个列表中:
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { _components.Add(middleware); return this; }
当调用app.Build()方法时,会逆序遍历这个中间件列表,并且将当前middleware包装成RequestDelegate的形式,传递给前一个middleware,由它决定是否要调用当前中间件。值得注意的是,Build方法里,默认在整个管道末端添加了一个返回404的中间件,所以如果不添加任何中间件,最后response返回的就是404(还没试验过)。源代码如下:
public RequestDelegate Build() { RequestDelegate app = context => { context.Response.StatusCode = 404; return Task.FromResult(0); }; foreach (var component in _components.Reverse()) { app = component(app); } return app; }
比如我们有三个中间件A, B, C,在StartUp.cs的Configure中依次添加这三个中间件。那么其实整个管道总共由4个中间件构成,依次是:A, B, C, 404,在我们调用Build()方法时,先创建一个404中间件的委托,然后将他作为next传给C,C再把自身作为next传给B,依此类推,最后返回的app就是A,此时A中已经有了B中间件的委托,可以选择自行选择调用B或者直接短路。
欢迎指正错误。
下一篇我们会详细介绍一下中间件的用法以及中间件包装的过程。