ASP.NET Core处理管道的深入理解

在 ASP.NET Core 的管道处理部分,实现思想已经不是传统的面向对象模式,而是切换到了函数式编程模式。这导致代码的逻辑大大简化,但是,对于熟悉面向对象编程,而不是函数式编程思路的开发者来说,是一个比较大的挑战。

处理请求的函数

在 ASP.NET Core 中,一次请求的完整表示是通过一个 HttpContext 对象来完成的,通过其 Request 属性可以获取当前请求的全部信息,通过 Response 可以获取对响应内容进行设置。

对于一次请求的处理可以看成一个函数,函数的处理参数就是这个 HttpContext 对象,处理的结果并不是输出结果,结果是通过 Response 来完成的,从程序调度的角度来看,函数的输出结果是一个任务 Task。

这样的话,具体处理 Http 请求的函数可以使用如下的 RequestDelegate 委托进行定义。

public delegate Task RequestDelegate(HttpContext context);

在函数参数 HttpContext 中则提供了此次请求的所有信息,context 的 Request 属性中提供了所有关于该次请求的信息,而处理的结果则在 context 的 Response 中表示。通常我们会修改 Response 的响应头,或者响应内容来表达处理的结果。

需要注意的是,该函数的返回结果是一个 Task,表示异步处理,而不是真正处理的结果。

参见:

我们从 ASP.NET Core 的源代码中选取一段作为参考,这就是在没有我们自定义的处理时,ASP.NET Core 最终的处理方式,返回 404。这里使用函数式定义。

RequestDelegate app = context => { // ...... context.Response.StatusCode = StatusCodes.Status404NotFound; return Task.CompletedTask; };

来源:在 GitHub 中查看 ApplicationBuilder 源码

把它翻译成熟悉的方法形式,就是下面这个样子:

public Task app(HttpContext context) { // ...... context.Response.StatusCode = StatusCodes.Status404NotFound; return Task.CompletedTask; };

这段代码只是设置了 Http 的响应状态码为 404,并直接返回了一个已经完成的任务对象。

为了脱离 ASP.NET Core 复杂的环境,可以简单地进行后继的演示,我们自定义一个模拟 HttpContext 的类型 HttpContextSample 和相应的 RequestDelegate 委托类型。

在模拟请求的 HttpContextSample 中,我们内部定义了一个 StringBuilder 来保存处理的结果,以便进行检查。其中的 Output 用来模拟 Response 来处理输出。

而 RequestDelegate 则需要支持现在的 HttpContextSample。

using System.Threading.Tasks; using System.Text; public class HttpContextSample { public StringBuilder Output { get; set; } public HttpContextSample() { Output = new StringBuilder(); } } public delegate Task RequestDelegate(HttpContextSample context);

这样,我们可以定义一个基础的,使用 RequestDelegate 的示例代码。

// 定义一个表示处理请求的委托对象 RequestDelegate app = context => { context.Output.AppendLine("End of output."); return Task.CompletedTask; }; // 创建模拟当前请求的对象 var context1 = new HttpContextSample(); // 处理请求 app(context1); // 输出请求的处理结果 Console.WriteLine(context1.Output.ToString());

执行之后,可以得到如下的输出

End of output.

处理管道中间件

所谓的处理管道是使用多个中间件串联起来实现的。每个中间件当然需要提供处理请求的 RequestDelegate 支持。在请求处理管道中,通常会有多个中间件串联起来,构成处理管道。

ASP.NET Core处理管道的深入理解

但是,如何将多个中间件串联起来呢?

可以考虑两种实现方式:函数式和方法式。

方法式就是再通过另外的方法将注册的中间件组织起来,构建一个处理管道,以后通过调用该方法来实现管道。而函数式是将整个处理管道看成一个高阶函数,以后通过调用该函数来实现管道。

方法式的问题是在后继中间件处理之前需要一个方法,后继中间件处理之后需要一个方法,这就是为什么 ASP.NET Web Form 有那么多事件的原因。

如果我们只是把后继的中间件中的处理看成一个函数,那么,每个中间件只需要分成 3 步即可:

前置处理

调用后继的中间件

后置处理

在 ASP.NET Core 中是使用函数式来实现请求的处理管道的。

在函数式编程中,函数本身是可以作为一个参数来进行传递的。这样可以实现高阶函数。也就是说函数的组合结果还是一个函数。

对于整个处理管道,我们最终希望得到的形式还是一个 RequestDelegate,也就是一个对当前请求的 HttpContext 进行处理的函数。

本质上来讲,中间件就是一个用来生成 RequestDelegate 对象的生成函数。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wswssx.html