ASP.NET Core 中的中间件
前言
??由于是第一次寫博客,如果您看到此文章,希望大家抱著找錯誤、批判的心態(tài)來看。 sky!
何為中間件?
在 ASP.NET Framework 中應(yīng)該都知道請求管道。可參考:淺談 ASP.NET 的內(nèi)部機制?系列,個人感覺超詳細。
題外話:
說到請求管道,就想以前還是超菜鳥時有次面試被問到這個問題,一臉懵逼只說了 Controller→Action→View。臉紅啊!!
ASP.NET Core 中的中間件就是.net framework 請求管道的實現(xiàn)。下圖演示了 Middlerware 的概念。 沿黑色箭頭執(zhí)行。
每一個中間件(Middleware1、Middleware2...)都是一個委托,這一系列委托就組成了整個管道。
中間件的寫法
直接在Startup.cs類的Configure方法里寫
app.Use(async (context, next) => { ? ?logger.LogInformation("中間件開始..."); ??await next.Invoke(); //執(zhí)行下一個中間件logger.LogInformation("中間件完成..."); });
結(jié)合上圖:
//logic對應(yīng)logger.LogInformation("中間件開始...");
next();對應(yīng)await next.Invoke();
//more logic對應(yīng)logger.LogInformation("中間件完成...");
其中//logic(即請求)是順序執(zhí)行。即:Middleware1→Middleware2→...→Middlewaren
而//more logic(即響應(yīng))是倒序執(zhí)行。即:Middlewaren→...→Middleware2→Middleware1
同 1,只是不用 Use 而是用 Run:
app.Run(async context =>{ ?? await context.Response.WriteAsync("請求終止了,下一步將會執(zhí)行已執(zhí)行過的Middleware的 //more logic");});
Run 會終止請求,即管道中最后一個中間件,后面詳細剖析!
下面這種寫法應(yīng)該是比較合理的,也是比較優(yōu)雅的
新建一個類如下(該類是有強制規(guī)范的,詳細見下文):
public class RequestTestMiddleware{ ? ?private readonly RequestDelegate _next; ?
?public RequestTestMiddleware(RequestDelegate next) ? ?{_next = next;} ?
?public async Task InvokeAsync(HttpContext context) ? ?{ ? ?
? ? ?//中間件開始 logicawait _next(context);//執(zhí)行下一個中間件//中間件完成 more logic} }
在Startup.cs類的Configure方法里添加如下代碼,效果和 1 相同:
app.UseMiddleware<RequestTestMiddleware>(); //app.UseMiddleware<RequestTestMiddleware>(params object[] parameters);//參數(shù)說明見下面不知發(fā)現(xiàn)了沒,上面的InvokeAsync方法不是用的打印日志,而是用的注釋。
因為我們沒有引用logger對象,了解過 ASP.NET Core 的肯定知道依賴注入,我們只需要把ILogger注入進來就行了,改造如下:
private readonly RequestDelegate _next; ? ?
public RequestTestMiddleware(RequestDelegate next) ? ? {_next = next;} ? ?
public async Task InvokeAsync(HttpContext context, ILogger<TestMiddleware> logger) ? ? {logger.LogInformation("中間件開始 logic"); ? ? ? ? await _next(context);logger.LogInformation("中間件完成 more logic");}}
通過依賴注入方法添加中間件:
新建類 TestMiddleware.cs?注意依賴注入的位置和 3 不同
private readonly ILogger _logger; ?
? public TestMiddleware(ILogger<TestMiddleware> logger) ? ? {_logger = logger;} ? ?
public async Task InvokeAsync(HttpContext context, RequestDelegate next) ? ? {_logger.LogInformation("中間件開始"); ? ? ?
? await next(context);_logger.LogInformation("中間件完成");}}
在Startup.cs類的ConfigureServices方法里添加如下代碼:
services.AddTransient<TestMiddleware>();在Startup.cs類的Configure方法里添加如下代碼:
app.UseMiddleware<TestMiddleware>();還有一種第三方容器激活中間件
源代碼分析(部分)
Run和Use的實現(xiàn)
直接放出源代碼:
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);} public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware){ ? ? return app.Use(next =>{ ? ? ? ? return context =>{Func<Task> simpleNext = () => next(context); ? ? ? ? ? ? return middleware(context, simpleNext);};});}2 個方法最終調(diào)用的都是app.Use(),我們看下代碼:
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware){_components.Add(middleware); ??return this; }
_components是IList<Func<RequestDelegate, RequestDelegate>>類型,其實就是把我們的Middleware添加到?_components?中,繼續(xù)看代碼:
public RequestDelegate Build(){RequestDelegate app = context =>{context.Response.StatusCode = 404; ? ?? ?return Task.CompletedTask;}; ?
?foreach (var component in _components.Reverse()){app = component(app);} ? ?return app; }
該方法會在Program.cs中Main方法的?CreateWebHostBuilder(args).Build().Run();?的?Run()?方法執(zhí)行。
此方法把我們所有的Middleware再次組裝成 1 個新的RequestDelegate,最終的順序?qū)?#xff1a;
Middleware1() { ? ?next()=>Middleware2(){ ? ? ? ?? ? ? ?next()=>Middleware3(){ ? ? ? ? ? ? ? ? ? ? ? ? ? ?next()=>最后的那個返回404的委托}} }
不知道寫清楚了沒( ╯□╰ ). 其中next()=>Middleware2()的意思為:next()就是?Middleware2()
繼承 IMiddleware 和沒繼承 IMiddleware(根據(jù)規(guī)范必須要有 InvokeAsync 或 Invoke 方法等)的區(qū)別:
按功能實現(xiàn)方面來說是沒區(qū)別的,但按性能方面應(yīng)該是繼承了 IMiddleware 的方式要好很多,因為沒繼承 IMiddleware 的方式會用到反射。(未測試,由于繼承 IMiddleware 還需要用依賴注入這里只是猜測)
代碼見:
Microsoft.AspNetCore.Http.Abstractions\Extensions\UseMiddlewareExtensions.cs 的 UseMiddleware 方法。
未繼承 IMiddleware 時的約定,直接看代碼吧:
//1.在middleware中必須存在public且有返回值的方法var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);//2.必須有‘Invoke’或‘InvokeAsync’方法var invokeMethods = methods.Where(m =>string.Equals(m.Name, "Invoke", StringComparison.Ordinal)|| string.Equals(m.Name, "InvokeAsync", StringComparison.Ordinal)).ToArray();//3.‘Invoke’和‘InvokeAsync’只能有1個if (invokeMethods.Length > 1) {}//4.‘Invoke’和‘InvokeAsync’必須要存在if (invokeMethods.Length == 0) {}var methodInfo = invokeMethods[0];//5.返回結(jié)果類型必須為Taskif (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType)){}中間件傳參
直接上代碼:
在Startup.cs類的Configure方法里:
//參數(shù)類型為: params object[] args app.UseMiddleware<RequestTestMiddleware>(1);具體實現(xiàn)方式同樣在 Microsoft.AspNetCore.Http.Abstractions\Extensions\UseMiddlewareExtensions.cs 的 UseMiddleware 方法中
高級用法 Map MapWhen
Map
app.Map("/map", _app => {_app.Run(async context =>{ ? ? ? ?await context.Response.WriteAsync("Test Map!");}); });當訪問https://localhost:5001/map時將返回 Test Map!
這里說一下,代碼中并沒有 MapController....
MapWhen
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), _app => {_app.Run(async context =>{ ? ? ? ?await context.Response.WriteAsync("Test Map!");}); });看源代碼會發(fā)現(xiàn),MapWhen 的第二個參數(shù)(委托)并不是上面Use()中next(),而是存在MapOptions的Branch屬性中,也是RequestDelegate委托
其他說明
Middleware 的執(zhí)行的有順序的,在合適的 Middleware 返回請求可時管道更短,速度更快。
比如 UseStaticFiles(),靜態(tài)資源不必走驗證、MVC 中間件,所以該方法在中間件的前面執(zhí)行。
我們看到有很多內(nèi)置的中間件的用法是*Use**,其實是加了個擴展:
總結(jié)
??第一次寫博客,最大的感觸就是慢,然后就是思維邏輯有點混亂,總想用最簡單的語言來表達,就是掌握不好。最后看起來還是太啰嗦了點。最后說明,以上很可能有錯誤的說法,希望大家以批判的角度來看,有任何問題可在留言區(qū)留言!Thanks!
相關(guān)文章:
ASP.NET Core URL Rewrite中間件
.Net Core Cors中間件解析
中間件中渲染Razor視圖
中間件實現(xiàn)服務(wù)端靜態(tài)化緩存
[譯]ASP.NET Core 2.0 帶初始參數(shù)的中間件
學(xué)習(xí)ASP.NET Core,怎能不了解請求處理管道[1]: 中間件究竟是個什么東西?
基于.NET CORE微服務(wù)框架 -談?wù)凜ache中間件和緩存降級
如何一秒鐘從頭構(gòu)建一個 ASP.NET Core 中間件
學(xué)習(xí)ASP.NET Core,你必須知道“中間件”是什么?中間件如何注冊?請求處理管道是如何通過中間件構(gòu)建的?
原文地址:https://www.cnblogs.com/dudd/p/9670028.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 中的中间件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET西安社区 [拥抱开源,又见 .N
- 下一篇: .NET Core中的性能测试工具Ben