ASP.NET Core应用程序提供了处理每个请求的完整控制。在这个请求管道中,我们可以动态配置各种业务逻辑对应的中间件(middleware),从而达到服务端可以针对不同用户做出不同的请求响应。
一、管道
ASP.NET Core应用程序在创建程序宿主之前需要构建一个管道。而IApplicationBuilder 是用来构建请求管道的.而请求管道,本质上就是对 HttpContext 的一系列操作,即通过对 Request 的处理,来生成 Reponse。
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; } // // 摘要: // 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); // // 摘要: // 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(); // // 摘要: // Builds the delegate used by this application to process HTTP requests. // // 返回结果: // The request handling delegate. RequestDelegate Build(); } }
从上面接口源代码中,IApplicationBuilder提供了几个管道构建的方法
1、Use 注册中间件
Use
是我们非常熟悉的注册中间件的方法,就是将注册的中间件保存到其内部属性 _components
中。注册多个中间件的时候围绕着Next
分别对Request和Respone做出相应的处理,B的执行会嵌套在A的里面,因此A是第一个处理Request,
并且最后一个收到Respone,这样就构成一个经典的的U型管道。
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { _components.Add(middleware); return this; }
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 }; });
var app = builder.Build(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting();
2、Build 创建一个请求管道
Build
方法创建一个 RequestDelegate 类型的委托。可以看到首先定义了一个 404
的中间件,然后使用了Reverse
函数将注册的中间件列表进行反转,因此首先执行我们所注册的最后一个中间件,输入参数便是一个 404
,
依次执行到第一个中间件,按我们的注册顺序从里到外,一层套一层。那么根据多中间件注册的u型管道模型,最后response的信息是第一个注册的中间件,即返回404。
public RequestDelegate Build()
{
RequestDelegate app = context =>
{
context.Response.StatusCode = 404;
return Task.CompletedTask;
};
foreach (var component in _components.Reverse())
{
app = component(app);
}
return app;
var builder = WebApplication.CreateBuilder(args); //创建程序宿主
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build(); //创建管道
3、Run 结束管道向下调用
在注册的中间件中,是通过 Next
委托串连起来的,如果在某一个中间件中没有调用 Next
委托,则该中间件将做为管道的终点。因此通过管道的串联调用,可以在管道中进行拦截,比如授权中间件,当授权成功后才进入Next,如果授权不成功则response。
如果流程正常走,那我们在最后一个中间件不应该再调用 Next
委托,而使用Run
扩展方法来注册最后一个中间件。
public static class RunExtensions { public static void Run(this IApplicationBuilder app, RequestDelegate handler) { if (app == null) { throw new ArgumentNullException(nameof(app)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } app.Use(_ => handler); } }
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
二、中间件
1、app.UseMiddleware()自定义中间件委托函数
从Use方法中可以看到所谓的中间件实际上就是个委托(Func<RequestDelegate, RequestDelegate>) 。 ASP.NET Core 提供了一个更加具体的中间件的概念,我们在大部分情况下都会将中间件定义成一个单独的类型,使代码更加清晰。
因此一般都使用自定义中间件委托函数注入中间件。自定义使用中间件有两种方式:
- 通过继承IMiddleware实现(需要注入服务)
public class TestMiddleWare : IMiddleware { public Task InvokeAsync(HttpContext context, RequestDelegate next) { context.Response.WriteAsync("中间件类A"); return next(context); } }
services.AddSingleton<TestMiddleWare>();
app.UseMiddleware<TestMiddleWare>();
- 通过约定实现
public class TestMiddleWare { public readonly RequestDelegate _next; public TestMiddleWare(RequestDelegate next) { _next = next; } public Task InvokeAsync(HttpContext context) { context.Response.WriteAsync("中间件类A"); return _next(context); } }
app.UseMiddleware<TestMiddleWare>();