OWIN的作用
過去,IIS作為.NET開發者來說是最常用的Web Server(沒有之一),源於微軟產品的 緊耦合關係,我們不得不將Website、Web Application、Web API等部署在IIS上,事實上在2010年前並沒有什麼不妥,但隨著近些年來Web的發展,特別是移動網際網路飛速發展,IIS作為Web Server已經暴露出他的不足了。主要體現在兩個方面,ASP.NET (System.Web)緊耦合IIS,IIS緊耦合OS,這就意味著,我們的Web Framework必須部署在微軟的作業系統上,難以 跨平台。
ASP.NET和IIS
我們知道,不管是ASP.NET MVC還是ASP.NET WEB API等都是基於ASP.NET Framework的,這種關係從前綴就可以窺倪出來。而ASP.NET的核心正是System.Web這個程式集,而且System.Web緊耦合IIS,他存在於.NET Framework中。所以,這導致了Web Framework嚴重的局限性:
•ASP.NET的核心System.Web,而System.Web緊耦合IIS
•System.Web是.NET Framework重要組成,已有15年以上歷史,沉重、冗餘,性能差,難於測試,約2.5M
•System.Web要更新和發布新功能必須等待.NET Framework發布
•.但NET Framework是Windows的基礎,往往不會隨意更新。
所以要想獲取最新的Web Framework是非常麻煩的,幸運的事,微軟已經意識到了問題的嚴重性,最新的Web Framework都是通過Nuget來獲取。
當然這是一部分原因,還有一層原因是ASP.NET & IIS實在太過於笨重,如何講呢?
複雜的生命周期已成為累贅?簡單來說,當請求到達伺服器時,Windows核心組件HTTP.SYS組件捕獲請求,他會分析請求並決定是否交給IIS來處理,當請求到達IIS之後,IIS會根據處理程式映射來匹配請求並交給對應的程式集(實現了ISAPI接口,比如我們熟知的aspnet_isapi.dll是專門用來處理ASP.NET Application)處理,最後載入了CLR運行環境,將請求交給aspnet_wp.exe去處理, 這時複雜的 ASP.NET生命周期往往令人頭大,但事實上有很多時候我們並不需要他。
如下圖所示ASP.NET Architecture:
打開IIS,你會發現他提供了非常豐富的功能:快取、身份驗證、壓縮、加密等。但隨著移動網際網路蓬勃的發展,特別是HTML 5越來越成熟的今天,我們看到越來越多的操作發生在客戶端,而不是沉重的從伺服器產生HTML返回, 更多的是通過異步 AJAX 返回原生的數據。同理,對於APP來說我們只需要Mobile Service返回數據。 顯然 IIS顯得笨重了點,而且IIS作為微軟產品系的一環,耦合程度太高。所以我們迫切需要輕量、快速、可擴展的宿主來承載Web Application和Web Service。
IIS和OS
IIS必須是安裝並運行在Windows作業系統中,這是微軟產品的一貫風格,環環相套,但不得不考慮他們的限制和局限性:
•IIS往往和作業系統(Windows Server)綁定在一起,這意味著對於一些新功能如WebSocket Protocol,我們不得不等待作業系統Windows Sever 2012、Windows 8的發布(IIS 8.0)。
•為了使用WebSocket這類新特性,他僅被IIS 8.0支持,如下所示:
這時你不得不去升級IIS,但升級作業系統可能會引發舊系統的不穩定性,所以要想平穩的升級IIS並不是簡單的。
•IIS作為經典的Web Server必須安裝在Windows系統中,Windows Server需要授權使用。
正是由於微軟產品繫緊耦合的關係,才造成跨平台上的不足,這也是被飽受詬病。 所以我們需要 OWIN來解耦,在面向對象的世界裡,接口往往是解耦的關鍵,如下圖所示:
使用OWIN,Web Framework不再依賴IIS和OS,這意味著你能使用任何你想的來替換IIS(比如:Katana或者Nowin),並且在必要時隨時升級,而不是更新作業系統。當然,如果你需要的話,你可以構建自定義的宿主和Pipeline去處理Http請求。
這一切的改變都是由於OWIN的出現,他提供了明晰的規範以便我們快速靈活的去擴展Pipeline來處理Http請求,甚至可以不寫任何一句代碼來切換不同的Web Server,前提是這些Web Server遵循OWIN規範。
回到頂部
OWIN的規範
現在我們已經了解了什麼是OWIN以及為什麼需要OWIN,現在是時候來分析一下OWIN的規範了。
OWIN Layers
實際上,OWIN的規範非常簡單,他定義了一系列的層(Layer),並且他們的順序是以堆(Stack)的形式定義,如下所示。OWIN中的接口被稱之為應用程式委託或者AppFunc,用來在這些層之間通信。
OWIN定義了4層:
Host:主要負責應用程式的配置和啟動進程, 包括初始化 OWIN Pipeline、運行Server。
Server:這是實際的Http Server,綁定套接字並監聽的HTTP請求然後將Request和Response的Body、Header封裝成符合OWIN規範的字典並傳送到OWIN Middleware Pipeline中,最後Application為Response Data填充合適的欄位輸出。
Middleware:稱之為中間件、組件,位於Server與Application之間,用來處理髮送到Pipeline中的請求,這類組件可以是簡單的Logger或者是複雜的Web Framework比如Web API、SignalR,只要Sever連線成功,Middleware中間件可以是任何實現應用程式委託的組件。
Application:這是具體的應用程式代碼,可能在Web Framework之上。對於Web API、SignalR這類Web Framework中間件而言,我們僅僅是改變了他們的託管方式,而不是取代ASP.NET WEB API、SignalR原先的應用程式開發。所以該怎么開發就怎么開發,只不過我們將他們註冊到OWIN Pipeline中去處理HTTP請求,成為OWIN管道的一部分,所以此處的Application即正在意義上的處理程式代碼。
Application Delegate
OWIN規範另一個重要的組成部分是接口的定義,用於Server和Middleware的互動。 他並不是嚴格意義上的接口,而是一個委託並且每個 OWIN中間件組件必須提供。
從字面上理解,每個OWIN中間件在必須有一個方法接受類型了IDictionary<string,object>的變數(俗稱環境字典),然後必須返回Task來異步執行。
Environment Dictionary
環境字典包含了Request、Response所有信息以及Server State。通過Pipeline,每箇中間件組件和層都可以添加額外的信息,但環境字典定義了一系列強制必須存在的Key,如下所示:
Request Data:
Required | Key Name | Value Description |
Yes | "owin.RequestBody" | AStreamwith the request body, if any.Stream.NullMAY be used as a placeholder if there is no request body. SeeRequest Body. |
Yes | "owin.RequestHeaders" | AnIDictionary<string, string[]>of request headers. SeeHeaders. |
Yes | "owin.RequestMethod" | Astringcontaining the HTTP request method of the request (e.g.,"GET","POST"). |
Yes | "owin.RequestPath" | Astringcontaining the request path. The path MUST be relative to the "root" of the application delegate; seePaths. |
Yes | "owin.RequestPathBase" | Astringcontaining the portion of the request path corresponding to the "root" of the application delegate; seePaths. |
Yes | "owin.RequestProtocol" | Astringcontaining the protocol name and version (e.g."HTTP/1.0"or"HTTP/1.1"). |
Yes | "owin.RequestQueryString" | Astringcontaining the query string component of the HTTP request URI, without the leading “?” (e.g.,"foo=bar&baz=quux"). The value may be an empty string. |
Yes | "owin.RequestScheme" | Astringcontaining the URI scheme used for the request (e.g.,"http","https"); seeURI Scheme. |
Response Data:
Required | Key Name | Value Description |
Yes | "owin.ResponseBody" | AStreamused to write out the response body, if any. SeeResponse Body. |
Yes | "owin.ResponseHeaders" | An IDictionary<string, string[]> of response headers. SeeHeaders. |
No | "owin.ResponseStatusCode" | An optionalintcontaining the HTTP response status code as defined inRFC 2616section 6.1.1. The default is 200. |
No | "owin.ResponseReasonPhrase" | An optionalstringcontaining the reason phrase associated the given status code. If none is provided then the server SHOULD provide a default as described inRFC 2616section 6.1.1 |
No | "owin.ResponseProtocol" | An optionalstringcontaining the protocol name and version (e.g."HTTP/1.0"or"HTTP/1.1"). If none is provided then the“owin.RequestProtocol”key’s value is the default. |
Other Data:
Required | Key Name | Value Description |
Yes | "owin.CallCancelled" | ACancellationTokenindicating if the request has been cancelled/aborted. SeeRequest Lifetime. |
Yes | "owin.Version" | Thestring"1.0"indicating OWIN version. SeeVersioning. |