正如上篇文章所述那樣,OWIN在Web Server與Web Application之間定義了一套規範(Specs),意在解耦Web Server與Web Application,
程序員
從而推動跨平臺的實現。若要真正使用OWIN規範,那麼必需要對他們進行實現。目前有兩個產品實現了OWIN規範——由微軟主導的
Katana和第三方的Nowin。這篇文章,我主要關注仍是Katana,由微軟團隊主導,開源到CodePlex上。
能夠在Visual Studio中輸入命令:git clone https://git01.codeplex.com/katanaproject來查看源代碼。
在介紹Katana以前,我以爲有必要爲你們梳理一下過去10幾年前ASP.NET 發展歷程。
設計模式
ASP.NET 發展歷程
ASP.NET Web Form
ASP.NET Web Form 在2002正式發佈時,面向的開發者主要有兩類:服務器
- 使用混合HTML標記和服務端腳本開發動態網站的ASP開發者,另外,ASP運行時抽象了底層的HTTP鏈接和Web Server,併爲開發者提供了一系列的對象模型用於交互Http請求,固然也提供了額外的服務諸如Session、Cache、State等。
- 開發WinForm的程序員,他們可能對HTTP和HTML一無所知,但熟悉拖控件的方式來構建應用程序。
爲了迎合這兩類開發者,ASP.NET Web Form經過使用沉重的ViewState來保存頁面回傳過程當中的狀態值,由於HTTP協議是無狀態的,經過ViewState,使本來沒有記憶的Http協議變得有記憶起來。這在當時是很是好的設計,能經過拖拽控件的形式快速開發Web,而沒必要過多的去關注底層原理。同時ASP.NET團隊還爲ASP.NET豐富了更多的功能、諸如:Session、Cache、Configuration等等。mvc
這在當時無疑是成功的,ASP.NET的發佈迅速拉攏了開發者,在Web開發中造成了一股新的勢力,但同時也買下來一些隱患:app
- 全部的功能、特性都發布在一個總體框架上而且緊耦合核心的Web抽象庫——System.Web
- System.Web是.NET Framework的重要組成部分,這意味着要修復更新System.Web必須更新.NET Framework,但.NET Framework是操做系統的基礎,爲了穩定性每每不會頻繁更新。
- ASP.NET Framework (System.Web)緊耦合IIS
- IIS只能運行在Windows系統
ASP.NET MVC
因爲Web Form產生一大堆ViewState和客戶端腳本,這對開發者來講慢慢變成一種累贅,由於咱們只想產生純淨的HTML標記。因此開發者更想去主動控制而非被動產生額外HTML標記。框架
因此微軟基於MVC設計模式推出了其重要的Web Framework——ASP.NET MVC Framework,經過Model-View-Control解耦了業務邏輯和表現邏輯,同時沒有了服務器端控件,將頁面的控制權徹底交給了開發者。異步
爲了快速更新迭代,經過Nuget來獲取更新,故從.NET Framework中分離開了。模塊化
但惟一不足的是,ASP.NET MVC仍是基於ASP.NET Framework(注:ASP.NET MVC 6已經不依賴System.Web),因此Web Application和Web Server依舊沒有解耦。
ASP.NET Web API
隨着時間的推移,一些問題開始暴露出來了,因爲Web Server和Web Application緊耦合在一塊兒,微軟在開發獨立、簡單的Framework上愈加捉襟見肘,這和其餘平臺下開源社區蓬勃發展造成鮮明對比,幸運的是,微軟作出了改變,推出了獨立的Web Framework ——ASP.NET Web API,他適用於移動互聯網並能夠快速經過Nuget安裝,更爲重要的是,他不依賴System.Web,也不依賴IIS,你能夠Self-Host或者在其餘Web Server部署。
Katana
隨着Web API可以運行在本身的輕量級的宿主中,而且愈來愈多簡單、模塊化、專注的Framework問世,開發人員有時候不得不啓動單獨的進程來處理Web應用程序的各類組件(模塊)、如靜態文件、動態文件、Web API和Socket。爲了不進程擴散,全部的進程必須啓動、中止而且獨立進行管理。這時,咱們須要一個公共的宿主進程來管理這些模塊。
這就是OWIN誕生的緣由,解耦成最小粒度的組件,而後這些標準化框架和組件能夠很容易地插入到OWIN Pipeline中,從而對組件進行統一管理。而Katana正是OWIN的實現,爲咱們提供了豐富的Host和Server。
走進Katana的世界
Katana做爲OWIN的規範實現,除了實現Host和Server以外,還提供了一系列的API幫助開發應用程序,其中已經包括一些功能組件如身份驗證(Authentication)、診斷(Diagnostics)、靜態文件處理(Static Files)、ASP.NET Web API和SignalR的綁定等。
Katana的基本原則
- 可移植性:從HostàServeràMiddleware,每一個Pipeline中的組件都是可替換的,而且第三方公司和開源項目的Framework都是能夠在OWIN Server上運行,也就是說不受平臺限制,從而實現跨平臺。
- 模塊化:每個組件都必須保持足夠獨立性,一般只作一件事,以混合模塊的形式來知足實際的開發需求
- 輕量和高效:由於每個組件都是模塊化開發,並且能夠輕鬆的在Pipeline中插拔組件,實現高效開發
Katana 體系結構
Katana實現了OWIN的Layers,因此Katana的體系結構和OWIN一致,以下所示:
1.)Host :宿主Host被OWIN規範定義在第一層(最底層),他的職責是管理底層的進程(啓動、關閉)、初始化OWIN Pipeline、選擇Server運行等。
Katana爲咱們提供了3中選擇:
- IIS / ASP.NET :使用IIS是最簡單和向後兼容方式,在這種場景中OWIN Pipeline經過標準的HttpModule和HttpHandler啓動。使用此Host你必須使用System.Web做爲OWIN Server
- Custom Host :若是你想要使用其餘Server來替換掉System.Web,而且能夠有更多的控制權,那麼你能夠選擇建立一個自定義宿主,如使用Windows Service、控制檯應用程序、Winform來承載Server。
- OwinHost :若是你對上面兩種Host還不滿意,那麼最後一個選擇是使用Katana提供的OwinHost.exe:他是一個命令行應用程序,運行在項目的根部,啓動HttpListener Server並找到基於約束的Startup啓動項。OwinHost提供了命令行選項來自定義他的行爲,好比:手動指定Startup啓動項或者使用其餘Server(若是你不須要默認的HttpListener Server)。
2.)Server
Host以後的Layer被稱爲Server,他負責打開套接字並監聽Http請求,一旦請求到達,根據Http請求來構建符合OWIN規範的Environment Dictionary(環境字典)並將它發送到Pipeline中交由Middleware處理。Katana對OWIN Server的實現分爲以下幾類:
- System.Web:如前所述那樣,System.Web和IIS/ASP.NET Host二者彼此耦合,當你選擇使用System.Web做爲Server ,Katana System.Web Server把本身註冊爲HttpModule和HttpHandler而且處理髮送給IIS的請求,最後將HttpRequest、HttpResponse對象映射爲OWIN環境字典並將它發送至Pipeline中處理。
- HttpListener:這是OwinHost.exe和自定義Host默認的Server。
- WebListener:這是ASP.NET vNext默認的輕量級Server,他目前沒法使用在Katana中
3)Middleware
Middleware(中間件)位於Host、Server以後,用來處理Pipeline中的請求,Middleware能夠理解爲實現了OWIN應用程序委託AppFun的組件。
Middleware處理請求以後並能夠交由下一個Pipeline中的Middleware組件處理,即鏈式處理請求,經過環境字典能夠獲取到全部的Http請求數據和自定義數據。Middleware能夠是簡單的Log組件,亦能夠爲複雜的大型Web Framework,諸如:ASP.NET Web API、Nancy、SignlR等,以下圖所示:Pipeline中的Middleware用來處理請求:
4.)Application
最後一層即爲Application,是具體的代碼實現,好比ASP.NET Web API、SignalR具體代碼的實現。
如今,我想你應該瞭解了什麼事Katana以及Katana的基本原則和體系結構,那麼如今就是具體應用到實際當中去了。
使用ASP.NET/IIS託管Katana-based應用程序
- Visual Studio建立Web Application
-
Install-Package Microsoft.Owin.Host.SystemWeb添加Startup啓動類
- ASP.NET/IIS做爲Host
- System.Web做爲Server
在Startup的Configuration方法中實現OWIN Pipeline處理邏輯,以下代碼所示:
-
public class Startup
-
{
-
public void Configuration(IAppBuilder app)
-
{
-
app.Run(context =>
-
{
-
context.Response.ContentType = "text/plain";
-
return context.Response.WriteAsync("Hello World");
-
});
-
}
-
}
app.Run方法將一個接受IOwinContext對象最爲輸入參數並返回Task的Lambda表達式做爲OWIN Pipeline的最後處理步驟,IOwinContext強類型對象是對Environment Dictionary的封裝,而後異步輸出"Hello World"字符串。
細心的你可能觀察到,在Nuget安裝Microsoft.Owin.Host.SystemWeb程序集時,默認安裝了依賴項Microsoft.Owin程序集,正式它爲咱們提供了擴展方法Run和IOwinContext接口,固然咱們也可使用最原始的方式來輸出"Hello World"字符串,即Owin程序集爲咱們提供的最原始方式,這僅僅是學習上參考,雖然咱們不會在正式場景下使用:
-
using AppFunc = Func<IDictionary<string, object>, Task>;
-
public class Startup
-
{
-
public void Configuration(IAppBuilder app)
-
{
-
app.Use(new Func<AppFunc, AppFunc>(next => (env =>
-
{
-
string text = "Hello World";
-
var response = env["owin.ResponseBody"] as Stream;
-
var headers = env["owin.ResponseHeaders"] as IDictionary<string, string[]>;
-
headers["Content-Type"] = new[] { "text/plain" };
-
return response.WriteAsync(Encoding.UTF8.GetBytes(text), 0, text.Length);
-
})));
-
}
-
}
使用自定義Host(self-host)託管Katana-based應用程序
使用自定義Host託管Katana應用程序與使用IIS託管差異不大,你可使用控制檯、WinForm、WPF等實現託管,但要記住,這會失去IIS帶有的一些功能(SSL、Event Log、Diagnostics、Management…),固然這能夠本身來實現。
- 建立控制檯應用程序
- Install-Package Microsoft.Owin.SelfHost
- 在Main方法中使用Startup配置項構建Pipeline並監聽端口
-
static void Main(string[] args)
-
{
-
using (WebApp.Start<Startup>("http://localhost:10002"))
-
{
-
System.Console.WriteLine("啓動站點:http://localhost:10002");
-
System.Console.ReadLine();
-
}
-
}
使用自定義的Host將失去IIS的一些功能,固然咱們能夠本身去實現。幸運的是,Katana爲咱們默認實現了部分功能,好比Diagnostic,包含在程序集Microsoft.Owin.Diagnostic中。
-
public void Configuration(IAppBuilder app)
-
{
-
app.UseWelcomePage("/");
-
app.UseErrorPage();
-
app.Run(context =>
-
{
-
//將請求記錄在控制檯
-
Trace.WriteLine(context.Request.Uri);
-
//顯示錯誤頁
-
if (context.Request.Path.ToString().Equals("/error"))
-
{
-
throw new Exception("拋出異常");
-
}
-
context.Response.ContentType = "text/plain";
-
return context.Response.WriteAsync("Hello World");
-
});
-
}
在上述代碼中,當請求的路徑(Request.Path)爲根目錄時,渲染輸出Webcome Page而且不繼續執行Pipeline中的其他Middleware組件,以下所示:
若是請求的路徑爲Error時,拋出異常,顯示錯誤頁,以下所示:
使用OwinHost.exe託管Katana-based應用程序
固然咱們還可使用Katana提供的OwinHost.exe來託管應用程序,毫無疑問,經過Nuget來安裝OwinHost。
若是你按照個人例子一步一步執行的話,你會發現無論使用ASP.NET/IIS託管仍是自託管,Startup配置類都是不變的,改變的僅僅是託管方式。同理OwinHost也是同樣的,但它更靈活,咱們可使用類庫或者Web應用程序來做爲Application。
- 使用類庫
類庫做爲Application,能夠最小的去引用程序集,建立一個類庫後,刪除默認的Class1.cs,而後而且添加Startup啓動項,這會默認像類庫中添加Owin和Microsoft.Owin程序集的引用。
而後,使用Nuget來安裝OwinHost.exe,如Install-Package OwinHost,注意它並非一個程序集,而是.exe應用程序位於<solution root>/packages/OwinHost.(version)/tools文件夾。
由於類庫不能直接運行,那麼只能在它的根目錄調用OwinHost.exe來託管,它將加載.\bin文件下全部的程序集,因此須要改變類庫的默認輸出,以下所示:
而後編譯解決方案,打開cmd,鍵入以下命令:
如上圖成功啓動了宿主Host而且默認監聽5000端口。
OwinHost.exe還提供自定義參數,經過追加-h來查看,以下所示:
既然類庫不能直接運行,固然你也不能直接進行調試,咱們能夠附加OwinHost進程來進行調試,以下所示:
注:
我在使用OwinHost.exe 3.0.1時,Startup若是是以下狀況下,它提示轉換失敗,不知是不是該版本的Bug。
-
using AppFunc = Func<IDictionary<string, object>, Task>;
-
public class Startup
-
{
-
public void Configuration(IAppBuilder app)
-
{
-
//使用OwinHost.exe,報錯,提示轉換失敗
-
app.Run(context=>context.Response.WriteAsync("Hello World"));
-
//使用OwinHost.exe 不報錯
-
//app.Use(new Func<AppFunc, AppFunc>(next => (env =>
-
//{
-
// string text = "Hello World";
-
// var response = env["owin.ResponseBody"] as Stream;
-
// var headers = env["owin.ResponseHeaders"] as IDictionary<string, string[]>;
-
// headers["Content-Type"] = new[] { "text/plain" };
-
// return response.WriteAsync(Encoding.UTF8.GetBytes(text), 0, text.Length);
-
//})));
-
}
-
}
報錯信息以下:
- 使用Web Application
Web Application比類庫使用起來輕鬆多了,你能夠直接運行和調試,惟一比較弱的多是它引用較多的程序集,你徹底能夠刪掉,好比System.Web。
經過Nuget安裝了OwinHost.exe以後,能夠在Web中使用它,以下所示:
幾種指定啓動項Startup的方法
- 默認名稱約束:默認狀況下Host會去查找root namespace下的名爲Startup的類做爲啓動項。
- OwinStartup Attribute:當建立Owin Startup類時,自動會加上Attribute 如:[assembly: OwinStartup(typeof(JKXY.KatanaDemo.OwinHost.Startup))]
- 配置文件,如: <add key="owin:appStartup" value="JKXY.KatanaDemo.Web.StartupDefault" />
- 若是使用自定義Host,那麼能夠經過WebApp.Start<Startup>("http://localhost:10002") 來設置啓動項。
- 若是使用OwinHost,那麼能夠經過命令行參數來實現,以下截圖所示
啓動項Startup的高級應用
啓動項Startup支持Friendly Name,經過Friendly Name咱們能夠動態切換Startup。
好比在部署時,咱們會有UAT環境、Production環境,在不一樣的環境中咱們能夠動態切換Startup來執行不一樣的操做。
舉個栗子,我建立來兩個帶有Friendly Name的Startup,以下所示:
- Production Startup
1 [assembly: OwinStartup("Production", typeof(JKXY.KatanaDemo.Web.StartupProduction))] 2 namespace JKXY.KatanaDemo.Web 3 { 4 using AppFunc = Func<IDictionary<string, object>, Task>; 5 public class StartupProduction 6 { 7 public void Configuration(IAppBuilder app) 8 { 9 app.Run(context=>context.Response.WriteAsync("Production")); 10 } 11 } 12 }
- UAT Startup
1 [assembly: OwinStartup("UAT",typeof(JKXY.KatanaDemo.Web.StartupUAT))] 2 3 namespace JKXY.KatanaDemo.Web 4 { 5 public class StartupUAT 6 { 7 public void Configuration(IAppBuilder app) 8 { 9 app.Run(context=>context.Response.WriteAsync("UAT")); 10 } 11 } 12 }
- 根據Friendly Name使用配置文件或者OwinHost 參數來切換Startup
<appSettings> <add key="owin:appStartup" value="Production" /> </appSettings>
小結
花了很久才最終完成這篇博客,爲你們講解了Katana的世界,那麼接下來我將繼續OWIN & Katana之旅,探索Middleware的建立,謝謝你們支持。