中間件基礎: 在.net6.0在請求在響應給請求者之前會通過請求管道再處理服務端的邏輯然後再響應給請求者,而請求管道則是由一系列中間件組成的有點類似於過濾器,為了更直觀的瞭解,我們請看下圖: 它可以決定是否將請求傳遞給請求管道中下一個中間件,也可以處理下一個中間件之前的邏輯也可以處理下一個中間件之後 ...
中間件基礎:
在.net6.0在請求在響應給請求者之前會通過請求管道再處理服務端的邏輯然後再響應給請求者,而請求管道則是由一系列中間件組成的有點類似於過濾器,為了更直觀的瞭解,我們請看下圖:
它可以決定是否將請求傳遞給請求管道中下一個中間件,也可以處理下一個中間件之前的邏輯也可以處理下一個中間件之後的邏輯.
此為中間件的註冊是有順序的,在定義時一定要註意.
中間件與過濾器的區別:
Filter是延續ASP.NET MVC的產物,同樣保留了五種的Filter,分別是Authorization Filter、Resource Filter、Action Filter、Exception Filter及Result Filter。具體你可以去查找一下關於ASP.NET CORE Filter的相關文章
根據描述,可以看出中間件和過濾器的功能類似,那麼他們有什麼區別?為什麼又要搞一個中間件呢?其實,過濾器和中間件他們的關註點是不一樣的,也就是說職責不一樣,乾的事情就不一樣。
同作為兩個AOP利器,Filter(過濾器)更貼合業務,它關註於應用程式本身,比如你看ActionFilter 和 ResultFilter,它都直接和你的Action,ActionResult交互了,是不是離你很近的感覺,那我有一些比如對我的輸出結果進行格式化,對我的請求的ViewModel進行數據驗證啦,肯定就是用Filter無疑了。它是MVC的一部分,它可以攔截到你Action上下文的一些信息,而中間件是沒有這個能力的。
可以看到,每一個中間件都都可以在請求之前和之後進行操作。請求處理完成之後傳遞給下一個請求
使用場景:
那麼,何時使用中間件呢?我的理解是在我們的應用程式當中和業務關係不大的一些需要在管道中做的事情可以使用,比如身份驗證,Session存儲,日誌記錄等。其實我們的 Asp.net core項目中本身已經包含了很多個中間件。比如 身份認證中間件 UseAuthorization()等系列
中間件管道:
Run: Run稱為終端中間件,也就是說該中間件就是管道的末尾,該中間件註冊之後,後面的中間件將不再執行,基本上放在末尾就行了,如下圖:
Use:通過該方法可以快速註冊一個匿名中間件,如下圖:
需要註意的是:
1.如果要將請求發送給管道中下一個中間件時,一定記得調用next(),否則會導致管道短路,後面的中間件不再被執行
2.在中間件中如有respance的操作,千萬不要調用next(),也不要對respanse進行任何更改,否則會拋出異常.
UseWhen:
該方法可以針對不同邏輯創建管道分支,擁有子級中間件,,支持嵌套,其實這裡和if else 同理,如下圖:
最後輸出:
UseWhen:Use Use Run
Map:
針對不同請求路徑創建管道分支,需要註意的是:一旦進入管道分支則不再回到主管道,如下圖:
當你訪問 /get/user 時,輸出如下:
Map get: Use Request Path: /user Request PathBase: /get Map get: Run
當你訪問 /post/user/student/1 時,則只會進入下麵的map 分支 其他不進入,具體場景交給你們自己嘗試哦
MapWhen:
與map類似,也是一旦進入分支則不會再回到主管道,只不過MapWhen不是基於路徑而是基於條件,也支持嵌套如下圖:
當你訪問 /get/user 時,輸出如下:
MapWhen get user: Use
基於約定的中間件:
"約定大於配置",有以下幾點:
1.擁有public構造函數,且該構造函數中至少包含一個類型為RequestDeleGet的參數
2.擁有名為Invoke或InvokeAsync的public方法,該方法必須包含類型為HttpContext 的參數,且該參數必須位於第一個位置,另外返回值必須為Task類型
3.在構造函數中的其他參數可以通過依賴註入(DI)填充,也可以通過UseMiddleware進行傳參填充
,通過DI填充時,只能接收 Transient 和 Singleton 的DI參數。這是由於中間件是在應用啟動時構造的(而不是按請求構造),所以當出現 Scoped 參數時,構造函數內的DI參數生命周期與其他不共用,如果想要共用,則必須將Scoped DI參數添加到Invoke/InvokeAsync
來進行使用。通過UseMiddleware
傳參時,構造函數內的DI參數和非DI參數順序沒有要求,傳入UseMiddleware
內的參數順序也沒有要求,但是我建議將非DI參數放到前面,DI參數放到後面。(這一塊感覺微軟做的好牛皮)Invoke/InvokeAsync
的其他參數也能夠通過依賴註入(DI)填充,可以接收 Transient、Scoped 和 Singleton 的DI參數。
示例如下:
然後,可以通過UseMiddleware方法將其添加到管道中
基於工廠的中間件:
優勢:
1.上面基於約定的中間件是單例的,但是基於工廠的中間件,可以再依賴註入時設置中間件實例生命周期.
2.使中間件強化了類型,因為實現了IMiddleware介面
該方式的實現基於IMiddlewareFactory
和IMiddleware
。先來看一下介面定義:
你有沒有想過當我們調用UseMiddleware時,它是如何工作的,事實上UseMiddleware
擴展方法會先檢查中間件是否實現了IMiddleware
介面。
如果實現了,則使用容器中註冊的IMiddlewareFactory實例來解析IMiddleware的實例("這下你應該知道為什麼稱為工廠中間件了吧"),如果沒實現
則使用基於約定的中間件邏輯來激活
註意,基於工廠的中間件,在應用的服務容器中一般註冊為 Scoped 或 Transient 服務。
這樣的話,咱們就可以放心的將 Scoped 服務註入到中間件的構造函數中了。
接下來,咱們就來實現一個基於工廠的中間件:
接下來,註入服務並註冊使用中間件:
基於約定的中間件 VS 基於工廠的中間件
- 基於約定的中間件實例都是 Singleton;而基於工廠的中間件實例可以是 Singleton、Scoped 和 Transient(當然,不建議註冊為 Singleton)
- 基於約定的中間件實例構造函數中可以通過依賴註入傳參,也可以用過
UseMiddleware
傳參;而基於工廠的中間件只能通過依賴註入傳參 - 基於約定的中間件實例可以在
Invoke/InvokeAsync
中添加更多的依賴註入參數;而基於工廠的中間件只能按照IMiddleware
的介面定義進行實現。
原文地址:https://www.cnblogs.com/shenweif/p/16717222.html
以上都是大家整理的一些基礎資料、可以參考看看,下麵進入正題
項目結構:
中間件的實現:
我這裡是計算了介面的耗時
啟用中間件:
方法1:(不推薦)直接啟用
方法2:(推薦)簡單封裝一下,再啟用,這種比較符合標準中間的寫法
中間件輸出的結果