ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

本文详述ASP.NET Core中的请求管道,包括IApplicationBuilder的Use、Build、Run和New方法,以及IMiddleware接口和UseMiddleware、UseWhen、MapWhen、UsePathBase和Map方法的使用。通过实例解析了中间件的执行流程和如何构建分支管道,展示了ASP.NET Core中灵活的请求处理机制。
摘要由CSDN通过智能技术生成

在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展。通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监听,并通过 HttpHandler 进入到我们的应用程序中。而在 ASP.NET Core 中,对请求管道进行了重新设计,通过使用一种称为中间件的方式来进行管道的注册,同时也变得更加简洁和强大。

IApplicationBuilder

在第一章中,我们就介绍过 IApplicationBuilder,在我们熟悉的 Startup 类的Configure方法中,通常第一个参数便是IApplicationBuilder,对它应该是非常熟悉了,而在这里,就再彻底的解剖一下 IApplicationBuilder 对象。

首先,IApplicationBuilder 是用来构建请求管道的,而所谓请求管道,本质上就是对 HttpContext 的一系列操作,即通过对 Request 的处理,来生成 Reponse。因此,在 ASP.NET Core 中定义了一个 RequestDelegate 委托,来表示请求管道中的一个步骤,它有如下定义:

public delegate Task RequestDelegate(HttpContext context);

而对请求管道的注册是通过 Func<RequestDelegate, RequestDelegate> 类型的委托(也就是中间件)来实现的。

为什么要设计一个这样的委托呢?让我们来分析一下,它接收一个 RequestDelegate 类型的参数,并返回一个 RequestDelegate 类型,也就是说前一个中间件的输出会成为下一个中间件的输入,这样把他们串联起来,形成了一个完整的管道。那么第一个中间件的输入是什么,最后一个中间件的输出又是如何处理的呢?带着这个疑惑,我们慢慢往下看。

IApplicationBuilder 的默认实现是 ApplicationBuilder,它的定义在 HttpAbstractions 项目中 :

public interface IApplicationBuilder{
    IServiceProvider ApplicationServices { get; set; }

    IFeatureCollection ServerFeatures { get; }

    IDictionary<string, object> Properties { get; }   

 IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);  
  IApplicationBuilder New();  
 
   RequestDelegate Build(); }
   public class ApplicationBuilder : IApplicationBuilder{  
   
    private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();    ... }

它有一个内部的 Func<RequestDelegate, RequestDelegate> 类型的集合(用来保存我们注册的中间件)和三个核心方法:

Use

Use是我们非常熟悉的注册中间件的方法,其实现非常简单,就是将注册的中间件保存到其内部属性 _components 中。

public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware){
    _components.Add(middleware);    return this;
}

我们使用Use注册两个简单的中间件:

public void Configure(IApplicationBuilder app){
    app.Use(next =>
    {
        Console.WriteLine("A");      
 return async (context) =>        {            // 1. 对Request做一些处理            // TODO            // 2. 调用下一个中间件            Console.WriteLine("A-BeginNext");          
   await next(context);            Console.WriteLine("A-EndNext");            // 3. 生成 Response            //TODO        };    });    app.Use(next =>    {        Console.WriteLine("B");    
       return async (context) =>        {            // 1. 对Request做一些处理            // TODO            // 2. 调用下一个中间件            Console.WriteLine("B-BeginNext");      
           await next(context);            Console.WriteLine("B-EndNext");          
            // 3. 生成 Response            //TODO        };    }); }

如上,注册了A和B两个中间件,通常每一个中间件有如上所示三个处理步骤,也就是围绕着Next分别对Request和Respone做出相应的处理,而B的执行会嵌套在A的里面,因此A是第一个处理Request,并且最后一个收到Respone,这样就构成一个经典的的U型管道。

而上面所示代码的执行结算如下:

非常符合我们的预期,但是最终返回的结果是一个 404 HttpNotFound,这又是为什么呢?让我们再看一下它的 Build 方法。

Build

第一章中,我们介绍到,在 Hosting 的启动中,便是通过该 Build 方法创建一个 RequestDelegate 类型的委托,Http Server 通过该委托来完成整个请求的响应,它有如下定义:

public RequestDelegate Build(){
    RequestDelegate app = context =>
    {
        context.Response.StatusCode = 404;      
       return Task.CompletedTask;    };  
   foreach (var component in _components.Reverse())    {        app = component(app);    }    return app; }

可以看到首先定义了一个 404 的中间件,然后使用了Reverse函数将注册的中间件列表进行反转,因此首先执行我们所注册的最后一个中间件,输入参数便是一个 404 ,依次执行到第一个中间件,将它的输出传递给 HostingApplication 再由 IServer 来执行。整个构建过程是类似于俄罗斯套娃,按我们的注册顺序从里到外,一层套一层。

最后,再解释一下,上面的代码返回404的原因。RequestDelegate的执行是从俄罗斯套娃的最外层开始,也就是从我们注册的第一个中间件A开始执行,A调用B,B则调用前面介绍的404 的中间件,最终也就返回了一个 404,那如何避免返回404呢,这时候就要用到 IApplicationBuilder 的扩展方法Run了。

Run

对于上面 404 的问题,我们只需要对中间件A做如下修改即可:

app.Use(next =>
{
    Console.WriteLine("B");    return async (context) =>
    {        // 1. 对Request做一些处理
        // TODO

        // 2. 调用下一个中间件
        Console.WriteLine("B-BeginNext");     
       await context.Response.WriteAsync("Hello ASP.NET Core!");        Console.WriteLine("B-EndNext");      
        // 3. 生成 Response        //TODO    }; });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值