日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

發布時間:2023/12/4 asp.net 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 ASP.NET 中,我們知道,它有一個面向切面的請求管道,有19個主要的事件構成,能夠讓我們進行靈活的擴展。通常是在?web.config?中通過注冊?HttpModule?來實現對請求管道事件監聽,并通過?HttpHandler?進入到我們的應用程序中。而在 ASP.NET Core 中,對請求管道進行了重新設計,通過使用一種稱為中間件的方式來進行管道的注冊,同時也變得更加簡潔和強大。

IApplicationBuilder

在第一章中,我們就介紹過?IApplicationBuilder,在我們熟悉的 Startup 類的Configure方法中,通常第一個參數便是IApplicationBuilder,對它應該是非常熟悉了,而在這里,就再徹底的解剖一下?IApplicationBuilder?對象。

首先,IApplicationBuilder?是用來構建請求管道的,而所謂請求管道,本質上就是對?HttpContext?的一系列操作,即通過對?Request?的處理,來生成?Reponse。因此,在 ASP.NET Core 中定義了一個?RequestDelegate?委托,來表示請求管道中的一個步驟,它有如下定義:

public delegate Task RequestDelegate(HttpContext context);

而對請求管道的注冊是通過?Func<RequestDelegate, RequestDelegate>?類型的委托(也就是中間件)來實現的。

為什么要設計一個這樣的委托呢?讓我們來分析一下,它接收一個?RequestDelegate?類型的參數,并返回一個?RequestDelegate?類型,也就是說前一個中間件的輸出會成為下一個中間件的輸入,這樣把他們串聯起來,形成了一個完整的管道。那么第一個中間件的輸入是什么,最后一個中間件的輸出又是如何處理的呢?帶著這個疑惑,我們慢慢往下看。

IApplicationBuilder 的默認實現是 ApplicationBuilder,它的定義在?HttpAbstractions?項目中 :

public interface IApplicationBuilder{IServiceProvider ApplicationServices { get; set; }IFeatureCollection ServerFeatures { get; }IDictionary<string, object> Properties { get; } ?

?IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); ?
??IApplicationBuilder New(); ?
?
? ?RequestDelegate Build(); }
? ?public class ApplicationBuilder : IApplicationBuilder{ ?
? ?
? ??private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();... }

它有一個內部的?Func<RequestDelegate, RequestDelegate>?類型的集合(用來保存我們注冊的中間件)和三個核心方法:

Use

Use是我們非常熟悉的注冊中間件的方法,其實現非常簡單,就是將注冊的中間件保存到其內部屬性?_components?中。

public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware){_components.Add(middleware); ? ?return this; }

我們使用Use注冊兩個簡單的中間件:

public void Configure(IApplicationBuilder app){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};});app.Use(next =>{Console.WriteLine("B"); ? ?
? ? ? ?return async (context) =>{ ? ? ? ? ? ?// 1. 對Request做一些處理// TODO// 2. 調用下一個中間件Console.WriteLine("B-BeginNext"); ? ? ?
? ? ? ? ? ?await next(context);Console.WriteLine("B-EndNext"); ? ? ? ? ?
? ? ? ? ? ??// 3. 生成 Response//TODO};}); }

如上,注冊了A和B兩個中間件,通常每一個中間件有如上所示三個處理步驟,也就是圍繞著Next分別對Request和Respone做出相應的處理,而B的執行會嵌套在A的里面,因此A是第一個處理Request,并且最后一個收到Respone,這樣就構成一個經典的的U型管道。

而上面所示代碼的執行結算如下:

非常符合我們的預期,但是最終返回的結果是一個?404 HttpNotFound,這又是為什么呢?讓我們再看一下它的?Build?方法。

Build

第一章中,我們介紹到,在?Hosting?的啟動中,便是通過該?Build?方法創建一個?RequestDelegate?類型的委托,Http Server 通過該委托來完成整個請求的響應,它有如下定義:

public RequestDelegate Build(){RequestDelegate app = context =>{context.Response.StatusCode = 404; ? ? ?
? ? ? ?return Task.CompletedTask;}; ?
? ?foreach (var component in _components.Reverse()){app = component(app);} ? ?return app; }

可以看到首先定義了一個?404?的中間件,然后使用了Reverse函數將注冊的中間件列表進行反轉,因此首先執行我們所注冊的最后一個中間件,輸入參數便是一個?404?,依次執行到第一個中間件,將它的輸出傳遞給?HostingApplication?再由?IServer?來執行。整個構建過程是類似于俄羅斯套娃,按我們的注冊順序從里到外,一層套一層。

最后,再解釋一下,上面的代碼返回404的原因。RequestDelegate的執行是從俄羅斯套娃的最外層開始,也就是從我們注冊的第一個中間件A開始執行,A調用B,B則調用前面介紹的404?的中間件,最終也就返回了一個?404,那如何避免返回404呢,這時候就要用到 IApplicationBuilder 的擴展方法Run了。

Run

對于上面?404?的問題,我們只需要對中間件A做如下修改即可:

app.Use(next => {Console.WriteLine("B"); ? ?return async (context) =>{ ? ? ? ?// 1. 對Request做一些處理// TODO// 2. 調用下一個中間件Console.WriteLine("B-BeginNext"); ? ?
? ? ? ?await context.Response.WriteAsync("Hello ASP.NET Core!");Console.WriteLine("B-EndNext"); ? ? ?
? ? ? ??// 3. 生成 Response//TODO}; });

將之前的?await next(context);?替換成了?await context.Response.WriteAsync("Hello ASP.NET Core!");,自然也就將404替換成了返回一個?"Hello ASP.NET Core!"?字符串。

在我們注冊的中間件中,是通過?Next?委托 來串連起來的,如果在某一個中間件中沒有調用?Next?委托,則該中間件將做為管道的終點,因此,我們在最后一個中間件不應該再調用?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);} }

可以看到,Run?方法接收的只有一個?RequestDelegate?委托,沒有了?Next?委托,進而保證了它不會再調用下一個中間件,即使我們在它之后注冊了其它中間件,也不會被執行。因此建議,我們最終處理?Response?的中間件使用?Run?來注冊,類似于 ASP.NET 4.x 中的?HttpHandler。

New

而?IApplicationBuilder?還有一個常用的?New?方法,通常用來創建分支:

public class ApplicationBuilder : IApplicationBuilder{ ?

?private ApplicationBuilder(ApplicationBuilder builder) ? ?{Properties = new CopyOnWriteDictionary<string, object>(builder.Properties, StringComparer.Ordinal);} ?
?
??public IApplicationBuilder New() ? ?{ ? ?
??? ?return new ApplicationBuilder(this);} }

New 方法根據自身來“克隆”了一個新的 ApplicationBuilder 對象,而新的 ApplicationBuilder 可以訪問到創建它的對象的?Properties?屬性,但是對自身?Properties?屬性的修改,卻不到影響到它的創建者,這是通過?CopyOnWriteDictionary?來實現的:

internal class CopyOnWriteDictionary<TKey, TValue> : IDictionary<TKey, TValue> { ?

?private readonly IDictionary<TKey, TValue> _sourceDictionary; ?
?public CopyOnWriteDictionary(IDictionary<TKey, TValue> sourceDictionary, IEqualityComparer<TKey> comparer) ? ?{_sourceDictionary = sourceDictionary;_comparer = comparer;} ?
?
? ?private IDictionary<TKey, TValue> ReadDictionary => _innerDictionary ?? _sourceDictionary; ? ?private IDictionary<TKey, TValue> WriteDictionary => { ? ? ? ?if (_innerDictionary == null){_innerDictionary = new Dictionary<TKey, TValue>(_sourceDictionary, _comparer);} ? ? ?
? ??return _innerDictionary;}; }

最后再放一張網上經典的 ASP.NET Core 請求管道圖:

IMiddleware

通過上面的介紹,我們知道,中間件本質上就是一個類型為?Func<RequestDelegate, RequestDelegate>?的委托對象,但是直接使用這個委托對象還是多有不便,因此 ASP.NET Core 提供了一個更加具體的中間件的概念,我們在大部分情況下都會將中間件定義成一個單獨的類型,使代碼更加清晰。

首先看一下?IMiddleware?接口定義:

public interface IMiddleware{ ?
? ??Task InvokeAsync(HttpContext context, RequestDelegate next); }

IMiddleware?中只有一個方法:InvokeAsync,它接收一個?HttpContext?參數,用來處理HTTP請求,和一個?RequestDelegate?參數,代表下一個中間件。當然, ASP.NET Core 并沒有要求我們必須實現?IMiddleware?接口,我們也可以像?Startup?類的實現方式一樣,通過遵循一些約定來更加靈活的定義我們的中間件。

UseMiddleware

對于?IMiddleware?類型的中間件的注冊,使用?UseMiddleware?擴展方法,定義如下:

public static class UseMiddlewareExtensions{ ?
?
? ?public static IApplicationBuilder UseMiddleware<TMiddleware>(this IApplicationBuilder app, params object[] args){ ? ? ?
? ? ?return app.UseMiddleware(typeof(TMiddleware), args);} public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args) ? ?{ ? ? ?
? ? ? ?if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo())){ ? ? ? ?
? ? ? ?? ?return UseMiddlewareInterface(app, middleware);}...} }

泛型的注冊方法,在 ASP.NET Core 中比較常見,比如日志,依賴注入中都有類似的方法,它只是一種簡寫形式,最終都是將泛型轉換為Type類型進行注冊。

如上代碼,首先通過通過?IsAssignableFrom?方法來判斷是否實現?IMiddleware?接口,從而分為了兩種方式實現方式,我們先看一下實現了?IMiddleware?接口的中間件的執行過程:

private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType){
? ?return app.Use(next =>{ ? ? ?
? ? ?return async context =>{ ? ? ? ?
? ? ?? ?var middlewareFactory = (IMiddlewareFactory)context.RequestServices.GetService(typeof(IMiddlewareFactory)); ? ? ? ? ? ?var middleware = middlewareFactory.Create(middlewareType); ? ? ? ? ? ?try{ ? ? ? ? ?
? ? ?? ? ? ? ?await middleware.InvokeAsync(context, next);} ? ? ? ? ?
? ? ?? ? ? ?finally{middlewareFactory.Release(middleware);}};}); }

如上,創建了一個?Func<RequestDelegate, RequestDelegate>?委托,在返回的?RequestDelegate?委托中調用我們的 IMiddleware 中間件的?InvokeAsync?方法。其實也只是簡單的對?Use?方法的一種封裝。而 IMiddleware 實例的創建則使用 IMiddlewareFactory 來實現的:

public class MiddlewareFactory : IMiddlewareFactory{ ?

?private readonly IServiceProvider _serviceProvider; ?
?
??public MiddlewareFactory(IServiceProvider serviceProvider) ? ?{_serviceProvider = serviceProvider;} ?
??
???public IMiddleware Create(Type middlewareType) ? ?{ ?
???
???? ? ?return _serviceProvider.GetRequiredService(middlewareType) as IMiddleware;} ?

??public void Release(IMiddleware middleware) ? ?{} }

通過如上代碼,可以發現一個坑,因為 IMiddleware 實例的創建是直接從 DI 容器中來獲取的,也就是說,如果我們沒有將我們實現了?IMiddleware?接口的中間件注冊到DI中,而直接使用?UseMiddleware?來注冊時,會報錯:“`InvalidOperationException: No service for type 'MiddlewareXX' has been registered.”。

不過通常我們并不會去實現?IMiddleware?接口,而是采用基于約定的,更加靈活的方式來定義中間件,而此時,UseMiddleware?方法會通過反射來創建中間件的實例:

public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args){ ?
?// 未實例 IMiddleware 時的注冊方式return app.Use(next =>{ ? ? ?
? ?var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); ? ? ?
? ? ?var invokeMethods = methods.Where(m => ? ? ?
? ? ?? ? ?string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)|| string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal)).ToArray();... ? ? ?
? ? ?? ? ? ?var methodinfo = invokeMethods[0]; ? ?
? ? ?? ? ? ?var parameters = methodinfo.GetParameters();
? ? ?? ? ? ?var ctorArgs = new object[args.Length + 1];ctorArgs[0] = next;Array.Copy(args, 0, ctorArgs, 1, args.Length); ? ?
? ? ?? ? ? ?var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs); ? ? ? ?
? ? ?? ? ? ?if (parameters.Length == 1){ ? ? ? ? ?
? ? ?? ? ? ??return (RequestDelegate)methodinfo.CreateDelegate(typeof(RequestDelegate), instance);} ? ? ?
? ? ?? ? ? ???var factory = Compile<object>(methodinfo, parameters); ? ? ? ?return context =>{ ? ? ? ? ? ?
? ? ?? ? ? ???return factory(instance, context, serviceProvider);};}); }

首先是根據命名約定來判斷我們的注冊的 Middleware 類是否符合要求,然后使用ActivatorUtilities.CreateInstance調用構造函數,創建實例。而在調用構造函數時需要的碼數,會先在傳入到?UseMiddleware?方法中的參數?args?中來查找 ,如果找不到則再去DI中查找,再找不到,將會拋出一個異常。實例創建成功后,調用Invoke/InvokeAsync方法,不過針對Invoke方法的調用并沒有直接使用反射來實現,而是采用表了達式,后者具有更好的性能,感興趣的可以去看完整代碼?UseMiddlewareExtensions?中的?Compile?方法。

通過以上代碼,我們也可以看出?IMiddleware?的命名約定:

  • 必須要有一個?Invoke?或?InvokeAsync?方法,兩者也只能存在一個。

  • 返回類型必須是?Task?或者繼承自?Task。

  • Invoke?或?InvokeAsync?方法必須要有一個?HttpContext 類型的參數。

不過,需要注意的是,Next?委托必須放在構造函數中,而不能放在?InvokeAsync?方法參數中,這是因為?Next?并不在DI系統中,而?ActivatorUtilities.CreateInstance?創建實例時,也會檢查構造中是否具有?RequestDelegate?類型的?Next?參數,如果沒有,則會拋出一個異常:“A suitable constructor for type '{instanceType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.”。

UseWhen

在有些場景下,我們可能需要針對某些請求,做一些特定的操作。當然,我們可以定義一個中間件,在中間件中判斷該請求是否符合我們的預期,進而選擇是否執行該操作。但是有一種更好的方式?UseWhen?來實現這樣的需求。從名字我們可以猜出,它提供了一種基于條件來注冊中間件的方式,有如下定義:

using Predicate = Func<HttpContext, bool>;

public static IApplicationBuilder UseWhen(this IApplicationBuilder app, Predicate predicate, Action<IApplicationBuilder> configuration){

? ?var branchBuilder = app.New();configuration(branchBuilder); ?
? ? ?return app.Use(main =>{branchBuilder.Run(main); ? ? ?
? ? ? ?var branch = branchBuilder.Build(); ?
? ? ? ??return context =>{ ? ? ? ? ?
? ? ? ???if (predicate(context)){ ? ? ? ? ?
? ? ? ???? ? ?return branch(context);} ? ? ? ? ? ?
? ? ? ???? ? ?else{ ? ? ? ? ? ? ?
? ? ? ???? ? ? ?return main(context);}};}); }

首先使用上面介紹過的?New?方法創建一個管道分支,將我們傳入的?configuration?委托注冊到該分支中,然后再將?Main?也就是后續的中間件也注冊到該分支中,最后通過我們指定的?Predicate?來判斷是執行新分支,還是繼續在之前的管道中執行。

它的使用方式如下:

public void Configure(IApplicationBuilder app){app.UseMiddlewareA();app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder =>{appBuilder.UseMiddlewareB();});app.UseMiddlewareC); }

我們注冊了三個中間件:A, B, C 。中間件 A 和 C 會一直執行(除了短路的情況), 而 B 只有在符合預期時,也就是當請求路徑以?/api?開頭時,才會執行。

UseWhen是非常強大和有用的,建議當我們想要針對某些請求做一些特定的處理時,我們應該只為這些請求注冊特定的中間件,而不是在中間件中去判斷請求是否符合預期來選擇執行某些操作,這樣能有更好的性能。

以下是?UseWhen?的一些使用場景:

  • 分別對MVC和WebAPI做出不同的錯誤響應。

  • 為特定的IP添加診斷響應頭。

  • 只對匿名用戶使用輸出緩存。

  • 針對某些請求進行統計。

MapWhen

MapWhen 與 UseWhen 非常相似,但是他們有著本質的區別,先看一下?MapWhen?的定義:

using Predicate = Func<HttpContext, bool>;

public static IApplicationBuilder MapWhen(this IApplicationBuilder app, Predicate predicate, Action<IApplicationBuilder> configuration){

? ?var branchBuilder = app.New();configuration(branchBuilder); ?
? ??var branch = branchBuilder.Build(); ?
? ?? ?// put middleware in pipelinevar options = new MapWhenOptions{Predicate = predicate,Branch = branch,}; ? ?return app.Use(next => new MapWhenMiddleware(next, options).Invoke); }

如上,可以看出他們的區別:MapWhen?并沒有將父分支中的后續中間件注冊進來,而是一個獨立的分支,而在?MapWhenMiddleware?中只是簡單的判斷是執行新分支還是舊分支:

public class MapWhenMiddleware{... ? ?public async Task Invoke(HttpContext context) ? ?{ ? ?

? ?if (_options.Predicate(context)){ ? ? ? ?
? ?? ?await _options.Branch(context);} ? ? ? ?
? ?? ?else{ ? ? ? ?
? ?? ?? ?await _next(context);}} }

再看一下?MapWhen?的運行效果:

public void Configure(IApplicationBuilder app){app.UseMiddlewareA();app.MapWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder =>{appBuilder.UseMiddlewareB();});app.UseMiddlewareC(); }

如上,中間件A將一直執行,之后如果請求路徑以?/api?開頭,則會執行?B?,并到此結束,不會再執行?C?,反之,不執行?B?,而執行?C?以及后續的其它的中間件。

當我們希望某些請求使用完全獨立的處理方式時,MapWhen?就非常有用,如?UseStaticFiles?:

public void Configure(IApplicationBuilder app){app.MapWhen(context => context.Request.Path.Value.StartsWithSegments("/assets"), appBuilder => appBuilder.UseStaticFiles()); }

如上,只有以?/assets?開頭的請求,才會執行?StaticFiles?中間件,而其它請求則不會執行?StaticFiles?中間件,這樣可以帶來稍微的性能提升。

UsePathBase

UsePathBase用于拆分請求路徑,類似于 MVC 中?Area?的效果,它不會創建請求管道分支,不影響管道的流程,僅僅是設置?Request?的?Path?和?PathBase?屬性:

public static IApplicationBuilder UsePathBase(this IApplicationBuilder app, PathString pathBase){pathBase = pathBase.Value?.TrimEnd('/'); ?
?if (!pathBase.HasValue){ ? ? ? ?return app;} ? ?
?
?return app.UseMiddleware<UsePathBaseMiddleware>(pathBase); }
?
?public class UsePathBaseMiddleware{ ?
?
??public async Task Invoke(HttpContext context) ? ?{ ? ? ?
??
?? ?if (context.Request.Path.StartsWithSegments(_pathBase, out matchedPath, out remainingPath)){ ? ? ? ?
?? ? ? ?? ?var originalPath = context.Request.Path; ? ?
?? ? ? ? ? ?var originalPathBase = context.Request.PathBase;context.Request.Path = remainingPath;context.Request.PathBase = originalPathBase.Add(matchedPath); ? ? ? ? ? ?try{ ? ? ? ? ? ?
?? ? ? ? ? ? ? ?await _next(context);} ? ? ? ? ?
?? ? ? ? ? ?finally{context.Request.Path = originalPath;context.Request.PathBase = originalPathBase;}} ? ?
?? ? ? ??else{ ? ? ? ? ?
?? ? ? ?? ?await _next(context);}} }

如上,當請求路徑以我們指定的?PathString?開頭時,則將請求的 PathBase 設置為 傳入的?pathBase,Path 則為剩下的部分。

PathString 用來表示請求路徑的一個片段,它可以從字符串隱式轉換,但是要求必須以?/?開頭,并且不以?/?結尾。

Map

Map 包含?UsePathBase?的功能,并且創建一個獨立的分支來完成請求的處理,類似于?MapWhen:

public static class MapExtensions{ ?

?public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration) ? ?{... ? ? ?
??return app.Use(next => new MapMiddleware(next, options).Invoke);} }

以上方法中與?MapWhen?一樣,不同的只是?Map?調用了?MapMiddleware?中間件:

public class MapMiddleware{... ?
?public async Task Invoke(HttpContext context) ? ?{PathString matchedPath;PathString remainingPath; ? ?
? ? ?if (context.Request.Path.StartsWithSegments(_options.PathMatch, out matchedPath, out remainingPath)){ ? ? ? ?
? ? ?? ?var path = context.Request.Path; ? ? ? ?
? ? ?? ?var pathBase = context.Request.PathBase;context.Request.PathBase = pathBase.Add(matchedPath);context.Request.Path = remainingPath;
? ? ?? ?? ?try{ ? ? ? ? ?
? ? ?? ?? ?? ? ?await _options.Branch(context);} ? ? ? ?
? ? ?? ??? ?finally{context.Request.PathBase = pathBase;context.Request.Path = path;}} ? ? ? ?
? ? ?? ?else{ ? ? ? ?
? ? ?? ? ?await _next(context);}} }

如上,可以看出?Map?擴展方法比?MapWhen?多了對?Request.PathBase?和?Request.Path?的處理,最后演示一下?Map?的用例:

public void Configure(IApplicationBuilder app){app.Map("/account", builder =>{builder.Run(async context =>{Console.WriteLine($"PathBase: {context.Request.PathBase}, Path: {context.Request.Path}"); ? ? ? ?
? ?await context.Response.WriteAsync("This is from account");});});app.Run(async context =>{Console.WriteLine($"PathBase: {context.Request.PathBase}, Path: {context.Request.Path}"); ? ?
? ?? ?await context.Response.WriteAsync("This is default");}); }

如上,我們為?/account?定義了一個分支,當我們?/account/user?的時候,將返回?This is from account?,并且會將 Request.PathBase 設置為?/account?,將 Request.Path 設置為?/user。

總結

本文詳細介紹了 ASP.NET Core 請求管道的構建過程,以及一些幫助我們更加方便的來配置請求管道的擴展方法。在 ASP.NET Core 中,至少要有一個中間件來響應請求,而我們的應用程序實際上只是中間件的集合,MVC 也只是其中的一個中間件而已。簡單來說,中間件就是一個處理http請求和響應的組件,多個中間件構成了請求處理管道,每個中間件都可以選擇處理結束,還是繼續傳遞給管道中的下一個中間件,以此串聯形成請求管道。通常,我們注冊的每個中間件,每次請求和響應均會被調用,但也可以使用?Map?,?MapWhen?,UseWhen?等擴展方法對中間件進行過濾。

參考資料:

  • conditional-middleware-based-on-request

  • asp-net-core-and-the-enterprise-part-3-middleware

相關文章:?

  • .NET Core 2.0 正式發布信息匯總

  • .NET Standard 2.0 特性介紹和使用指南

  • .NET Core 2.0 的dll實時更新、https、依賴包變更問題及解決

  • .NET Core 2.0 特性介紹和使用指南

  • Entity Framework Core 2.0 新特性

  • 體驗 PHP under .NET Core

  • .NET Core 2.0使用NLog

  • 升級項目到.NET Core 2.0,在Linux上安裝Docker,并成功部署

  • 解決Visual Studio For Mac Restore失敗的問題

  • ASP.NET Core 2.0 特性介紹和使用指南

  • .Net Core下通過Proxy 模式 使用 WCF

  • .NET Core 2.0 開源Office組件 NPOI

  • ASP.NET Core Razor頁面 vs MVC

  • Razor Page–Asp.Net Core 2.0新功能 ?Razor Page介紹

  • MySql 使用 EF Core 2.0 CodeFirst、DbFirst、數據庫遷移(Migration)介紹及示例

  • .NET Core 2.0遷移技巧之web.config配置文件

  • asp.net core MVC 過濾器之ExceptionFilter過濾器(一)

  • ASP.NET Core 使用Cookie驗證身份

  • ASP.NET Core MVC – Tag Helpers 介紹

  • ASP.NET Core MVC – Caching Tag Helpers

  • ASP.NET Core MVC – Form Tag Helpers

  • ASP.NET Core MVC – 自定義 Tag Helpers

  • ASP.NET Core MVC – Tag Helper 組件

  • ASP.NET Core 運行原理解剖[1]:Hosting

  • ASP.NET Core 運行原理解剖[2]:Hosting補充之配置介紹

原文地址:http://www.cnblogs.com/RainingNight/p/middleware-in-asp-net-core.html


.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

總結

以上是生活随笔為你收集整理的ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。