.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內置不少中間件,具體需要參考官網文檔。