.Net Core管道(pipeline)是什么?
简单来说,就是从发起请求到返回结果的一个过程,在.Net Core中这里面的处理是由中间件(middleware)来完成。
管道机制解释
用户在发起请求后,系统会自动生成一个请求管道(request pipeline),在这个请求管道中,可以通过run、map和use方法来配置请求委托,而在单独的请求委托中定义的可重用的类和并行的匿名方法即为中间件,也叫做中间件组件。具体流程如图。
从上图可以看出,当发起请求后,系统会创建一个请求管道,在这个管道中,每一个中间件都会按顺序处理(可能会执行,也可能不会被执行,取决于具体的业务逻辑),等最后一个中间件处理完后,又会按照相反的方向返回最终的处理结果。
官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0#built-in-middleware
代码阐述
创建一个.Net Core项目,编写以下代码。ps.例子用的.Net Core 5。
public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //context HttpContext //next 管道中的下一个委托 //Middleware 1 app.Use(async (context, next) => { Console.WriteLine("Middleware 1 start"); //调用下一个中间件 await next(); Console.WriteLine("Middleware 1 end"); }); //Middleware 2 app.Use(async (context, next) => { Console.WriteLine("Middleware 2 start"); //调用下一个中间件 await next(); Console.WriteLine("Middleware 2 end"); }); //Middleware 3 //run 终端中间件 app.Run(async (context) => { Console.WriteLine("request end"); await context.Response.WriteAsync("request end"); }); }
运行程序,然后打开http://127.0.0.1:xxxx,控制台得到结果。直接在浏览器打开该地址,可能会遇到重复进入中间件的问题,原因不明,可能做了多次请求。
使用postman做请求是正常的,得到了“request end”的返回结果。
中间件作为处理http请求和相应的组件,通过多个中间件之间的关系来形成管道。根据例子可以看出,中间件的执行顺序是1->2->3->2->1。
Run方法作为终端中间件,在按顺序执行到它后,就会终止管道,后面注册的中间件都不会执行。
Map 扩展用作约定来创建管道分支。 Map
基于给定请求路径的匹配项来创建请求管道分支。 如果请求路径以给定路径开头,则执行分支,通过不同请求路由,去执行不同的中间件。
//Middleware 1 app.Use(async (context, next) => { Console.WriteLine("Middleware 1 start"); //调用下一个中间件 await next(); Console.WriteLine("Middleware 1 end"); }); app.Map(new PathString("/path"), a => { a.Use(async (context, next) => { Console.WriteLine("path分支1 start"); await next(); Console.WriteLine("path分支1 end"); }); a.Use(async (context, next) => { Console.WriteLine("path分支2 start"); await next(); Console.WriteLine("path分支2 end"); }); a.Run(async context => { Console.WriteLine("path分支 end"); await context.Response.WriteAsync("path分支 end"); }); }); //Middleware 3 //run 终端中间件 app.Run(async (context) => { Console.WriteLine("request end"); await context.Response.WriteAsync("request end"); });
调用http://127.0.0.1:xxxx,得到结果。
调用http://127.0.0.1:xxxx/path,得到结果。
Map匹配到对应的路由后,会进入到一个新的分支,然后执行新分支里的中间件,执行完毕后,不会再返回到主的请求管道。
使用UseWhen来做路由匹配,也是基于路由来创建新的管道分支,执行完新分支的中间件后,如果里面中间件没短路或者没有终端中间件,会返回到主的请求管道。
//Middleware 1 app.Use(async (context, next) => { Console.WriteLine("Middleware 1 start"); //调用下一个中间件 await next(); Console.WriteLine("Middleware 1 end"); }); app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/path")), a => { a.Use(async (context, next) => { Console.WriteLine("path分支1 start"); await next(); Console.WriteLine("path分支1 end"); }); a.Use(async (context, next) => { Console.WriteLine("path分支2 start"); await next(); Console.WriteLine("path分支2 end"); }); }); //Middleware 3 //run 终端中间件 app.Run(async (context) => { Console.WriteLine("request end"); await context.Response.WriteAsync("request end"); });
调用http://127.0.0.1:xxxx/path,得到结果。
我们也可以自定义一个中间件来使用。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {//app.UseMiddleware<RequestCultureMiddleware>(); //等价于下述调用方式 app.UseRequestCulture(); //Middleware 3 //run 终端中间件 app.Run(async (context) => { Console.WriteLine("request end"); await context.Response.WriteAsync("request end"); }); } public static class RequestCultureMiddlewareExtensions {
//拓展方法,把自定义的中间件业务加进去 public static IApplicationBuilder UseRequestCulture( this IApplicationBuilder builder) { return builder.UseMiddleware<RequestCultureMiddleware>(); } } public class RequestCultureMiddleware { private readonly RequestDelegate _next; //注入next,使之能够和外面管道进行关联 public RequestCultureMiddleware(RequestDelegate next) { _next = next; }
//InvokeAsync用来处理相关业务,并进行中间件的连接。 public async Task InvokeAsync(HttpContext context) { Console.WriteLine("new middleware start"); await _next(context); Console.WriteLine("new middleware end"); } }
调用http://127.0.0.1:xxxx,得到结果。
.Net Core内置不少中间件,具体需要参考官网文档。