Asp.net core中間件實現原理及用法解說
簡述asp.net core中間件的實現思路
一次http請求的過程,就是對一個Request請求進行若干次邏輯處理,並最終設置Response的過程。從代碼的實現維度看,由於Request和Response都在HttpContext里,可將此過程表示為“以一個httpContext為輸入的委托函數”,即delegate Task RequestDelegate(HttpContext context),為方便此文的描述,我們將此委托函數暫時稱為“請求處理邏輯”
而中間件的作用,就是在請求的后面加入一個處理邏輯,這個處理邏輯是以“前一個請求處理邏輯”為輸入,並經過中間件自己的處理后,返回一個“新的請求處理邏輯”。所以從代碼上可將“中間件”表式為以一個“請求處理邏輯”為輸入並返回另一個“請求處理邏輯”的委托,即Func<RequestDelegate,RequestDelegate>。而多個中間件即表示為List<Func<RequestDelegate,RequestDelegate>>。
Asp.net core中間件的核心功能就是如何將一系列的中間件,合並成一個“請求處理邏輯”的過程,即如何將List<Func<RequestDelegate,RequestDelegate>>合並生成一個RequestDelegate。合並邏輯如下
上面的代碼有兩個地方要注意
1、asp.net core會默認在請求的最后加入一個“404”處理的中間件。
2、合並時,組件是先反序后再循環的
因先加入的中間件要先執行,所以在合並時,第一個中間件要最后合並,即要倒序后再循環合並中間件
如何使用中間件
使用中間件有四種方法:Use、Run、Map和使用Middleware class,但前三種方法最終調用的都是Use方法,我們來看看Use方法的實現邏輯,如下
即use方法只是在中間件列表(_components)的最后再加入一個中間件
下面詳細描述四種方法的用法
Use的用法
Ues的用法有兩種
用法一
調用IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware),此用法需在middleware委托里自己控制是否要進入下一個中間件,並且要自己創建一個RequestDelegate並返回,寫法會比較復雜。
示例如下
用法二
調用IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware),這是一個擴展方法,此方法不用自己創建RequestDelegate並返回,寫法比較簡潔。它最終調用的方法還是用法一中的實現,此方法的實現代碼如下。
示例如下
需注意:上面的兩種Use用法,在第二個中間件時,並沒有再調用下一個中間件,這是為了確保http請求不會進入到asp.net core默認的最后一個404中間件,因為最后一個404中間件設置了status code,而一但response body之前已經開始寫入時,是不能再改變status code或是request header的,否則會報錯。微軟的官方文檔里要求中間件的使用要遵循如下規則:如response body改變后就不要再調用下一個中間件,避免下一個中間件對上一個中間件的httpcontext內容的污染。(本文示例為演示目的,未遵循此約定)
run的用法
run方法的實現代碼如下
注意:從run方法的實現的代碼可以看出,run是不會再執行下一個中間件的,所以第一個中間run方法后面的中間件都不會起作用。所以一般用run時都是放在中間件的最后
map的用法
map其實准確來說不是中間件的用法,而是新開一個“中間件請求路線分支”,在這個“分支”里,可以再用use和run方法來組件一個新的中間件邏輯。
示例如下
如上示例,請求地址當能匹配上/test里,才會啟用map里的中間件
Middleware class的用法
Middleware class不需要繼承任何類或是接口,但必須有名為Invoke,返回類型為Task,且第一個參數為HttpContext類型的方法。
示例如下
Asp.net core內置中間件的介紹
中間件名稱 |
如何使用及說明 |
Authentication |
App.UseAuthentication,驗證當前請求的用戶,並設置HttpContext.User,當OAuth callbacks時,會中止執行下一個中間件。放到要用到用戶驗證的中間件前面 |
Static File |
app.UseStaticFiles(),判斷當前請求是否為靜態文件,如果是則中止執行下一個中間件,否則繼續下一個中間件。放到管道的最前 |
Response Caching |
app.UseResponseCaching(),緩存中間件 |
MVC |
app.UseMvc(),將MVC引入到中間件管道,如果請求的地址能找到對應的MVC路由,則中止執行下一個中間件。放到管道的最后。 |
Exception |
app.UseDeveloperExceptionPage();或app.UseExceptionHandler();用於處理程序的異常信息。放到管道的最前 |
Authorization |
授權中間件。不需直接引用,App.UseMvc()會在內部調用,並配合app.UseAuthentication()一起使用。 |
中間件的總結
1、通過use,run,map,middleware class四種方法使用
2、使用多個中間件時,需注意中間件的順序
3、在設計中間件時,請遵循“責任分離”原則,即一個中間件只對“單一責任”進行處理,如驗證用戶、授權等。
4、如果對response body做了修改后,請不要再執行下一個中間件