OWIN的理解和實踐(三) –Middleware開發入門
上篇我們談了Host和Server的建立,但Host和Server無法產出任何有實際意義的內容,真正的內容來自於加載於Server的Middleware,本篇我們就著重介紹下Middleware的開發入門.
Middleware是什麼
如果把HTTP互動理解為一次答題活動,那麼Request是問題,Response就是答案,Server是課堂,Middleware就是參與者,注意我這裡用的是參與而不是解答,因為我們允許有些Middleware不給出答案.
Middleware有什麼資源
要參與答題活動就必須有知識,也就是資源.在OWIN規則中,所有Middleware只能獲得並影響一個資源,這個就是OWIN Context,有一個Microsoft.Owin. IOwinContext介面定義了這個上下文的標準. 我們來看下這個裡面有什麼東西.
這個上下文介面中 提供的資源,是Middleware進行運作的關鍵:
- Authentication : 獲取可在當前請求上使用的身份驗證(Identity)中介軟體功能。通過這個屬性可以非常便捷在任何Middleware中訪問當前的Identity資訊.當然至少一個Identity中介軟體需要被載入,否則這個屬性中的內容沒有意義.
- Environment: 獲取已包裝的 OWIN 環境。它本質是一個數據字典,一個Middleware利用Key放入一個資訊,而另外一個Middleware根據Key拿出來使用. 和Session異曲同工.
- Request: 獲取可公開特定於請求的屬性的包裝。Middleware從這裡瞭解我們的提問者(Request)提供了那些資訊.
- Response : 獲取可公開特定於響應的屬性的包裝。Middleware通過這個屬性可以給出,影響或者改變我們的Response(答案),當然它也可以不做任何調整.
Middleware到底做什麼,怎麼做
簡單來說,Middleware可以做什麼怎麼做可以歸結為以下幾點:
- 獲得OWIN Context和它內部封裝的所有資訊.
- 從Request中獲取請求的所有資訊.
- 從Environment中獲取其他Middleware共享的資源,以便於和其他Middleware互動,或者使用其他Middleware提供的功能.
- 從Authentication中獲取當前的身份驗證資訊和結果.
- 通過Response給出,影響,甚至改變Server即將發出的”答案”
Middleware有哪幾種類型
剛剛我們說了,Middleware不一定是問題的解答者, 他們有前後的順序和各自回答問題的方式,根據他們的參與方式,我把他們分成3種情況:
- 解答者: 瞭解問題的內容(Request),給出最終答案(Response),一般不需要後續解答者的參與. 比較典型是解答者是WebApi和StaticFiles(靜態檔案).
- 參與者: 瞭解問題的內容,給出一定的資源(Environment)供其他參與者使用,本身一般不參與解答, 有可能在答案中加入一些附加資訊.比較典型的有Session和Identity.它們一般會加入一些Cookie但不影響Response實體內容.
- 監控者: 在其他參與者開始處理或者處理完畢的時候對當前的Context中的資訊進行處理,它也一般不參與解答,有可能在答案中加入一些附加資訊. 比較典型的有Logging, Diagnostics.
如何建立Middleware
建立一個的Middleware分以下幾個步驟:
- 引入Microsoft.Owin包
- 建立一個類
- 使這個類繼承Microsoft.Owin.OwinMiddleware
- 實現這個類的建構函式
- 覆蓋並實現父類的Invoke函式
一個最為典型的實現如下
using Microsoft.Owin; using System.Threading.Tasks; /// <summary> /// Middleware類必須繼承Microsoft.Owin.OwinMiddleware /// </summary> public class SampleMiddleware : OwinMiddleware { public SampleMiddleware(OwinMiddleware next) : base(next) { //建構函式 } public override Task Invoke(IOwinContext context) { //中介軟體的實現程式碼 return Next.Invoke(context); } }
絕大部分Middleware需要預設一些屬性,這些屬性可以通過改造建構函式來實現:
object m_Options; public SampleMiddleware(OwinMiddleware next,object options) : base(next) { //引入引數類,並可以再類中使用 m_Options = options; }
當然類似的options引數可以有多個.
以上的Middleware實現其實是沒有意義的,因為沒有做任何事情,下面我將給出一個”給出答案”的簡單實現,根據上面的描述,我在下面僅僅給出Invoke函式的內容.
這裡再插一句,上述程式碼中的next或Next指的是排在這個Middleware之後的另一個Middleware,而context就是我們上面所說的上下文資訊.
一個簡單的Middleware範例
剩下的工作,就是在Invoke函式中實現當前Middleware的功能,這裡給出一個非常簡單的實現,來做出一個最簡單的功能: 輸入結尾為\tick的URL,返回一個純文字的Response,裡面包含當前伺服器時間的Tick資訊.
public override Task Invoke(IOwinContext context) { PathString tickPath = new PathString("/tick"); //判斷Request路徑為/tick開頭 if (context.Request.Path.StartsWithSegments(tickPath)) { string content = DateTime.Now.Ticks.ToString(); //輸出答案--當前的Tick數字 context.Response.ContentType = "text/plain"; context.Response.ContentLength = content.Length; context.Response.StatusCode = 200; context.Response.Expires = DateTimeOffset.Now; context.Response.Write(content); //解答者告訴Server解答已經完畢,後續Middleware不需要處理 return Task.FromResult(0); } else //如果不是/tick路徑,那麼交付後續Middleware處理 return Next.Invoke(context); }
這裡提幾個要點:
- PathString是Miscrosoft.Owin下一個類,封裝了URL處理的一些功能.
- Task.FromResult(0) 表示一個空的Task,說明該Middleware在某些情況下不再觸發後續的Middleware執行—也就是”到此為止”.
- 最後Next.Invoke(context)是一個非常標準的實現,把上下文交付下一個Middleware繼續處理—相當於”交出接力棒”.
- 這個Middleware是一個標準的解答者.它給出了”/tick”這個問題的最終答案.
如何使用Middleware
這裡需要回到我的上一篇博文, Host和Server開發, 在那裡面,我說到目前的Startup函式是空的,說明沒有載入任何Middleware,而現在我們需要在那個函式裡面載入我們開發的Middleware了,程式碼很簡單:
private static void Startup(Owin.IAppBuilder app) { //載入Sample Middleware Console.WriteLine("Sample Middleware loaded..."); app.Use<SampleMiddleware>(); }
注意2點:
- 保證已經加入了 using Owin;
- SampleMiddleware的建構函式是僅有一個OwinMiddleware引數的版本,如果有附加引數,請加到Use函式的引數列表裡面去.
好了,聯合上一篇博文的程式碼,編譯執行.我們能夠看到如下輸出介面:
(注意:很多機器需要管理員許可權執行VS,才能正常執行該程式)
我們看到了一個tick. 這就是這個中介軟體返回的結果.而其他地址依然會沒有任何返回,這是因為並沒有任何其他Middleware來處理其他的情況.
當然,基於OWIN架設的體系,我們可以開發更加複雜的Middleware,下一篇,我將會進一步給出三個比較複雜的Middleware實現: StaticFile, Session, Logging; 來幫助大家進一步理解,解答者,參與者和監控者的概念.同時也深入理解Middleware的運作機制.