ASP.NET MVC隨想錄——鋒利的KATANA

【轉自】ASP.NET MVC隨想錄——鋒利的KATANAhtml

 
 
  

正如上篇文章所述那樣,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處理邏輯,以下代碼所示:

  1. public class Startup
  2.     {
  3.         public void Configuration(IAppBuilder app)
  4.         {
  5.             app.Run(context =>
  6.             {
  7.                 context.Response.ContentType = "text/plain";
  8.                 return context.Response.WriteAsync("Hello World");
  9.             });
  10.         }
  11.     }

app.Run方法將一個接受IOwinContext對象最爲輸入參數並返回Task的Lambda表達式做爲OWIN Pipeline的最後處理步驟,IOwinContext強類型對象是對Environment Dictionary的封裝,而後異步輸出"Hello World"字符串。

細心的你可能觀察到,在Nuget安裝Microsoft.Owin.Host.SystemWeb程序集時,默認安裝了依賴項Microsoft.Owin程序集,正式它爲咱們提供了擴展方法Run和IOwinContext接口,固然咱們也可使用最原始的方式來輸出"Hello World"字符串,即Owin程序集爲咱們提供的最原始方式,這僅僅是學習上參考,雖然咱們不會在正式場景下使用:

  1. using AppFunc = Func<IDictionary<string, object>, Task>;
  2.     public class Startup
  3.     {
  4.         public void Configuration(IAppBuilder app)
  5.         {
  6.             app.Use(new Func<AppFunc, AppFunc>(next => (env =>
  7.             {
  8.                 string text = "Hello World";
  9.                 var response = env["owin.ResponseBody"] as Stream;
  10.                 var headers = env["owin.ResponseHeaders"] as IDictionary<string, string[]>;
  11.                 headers["Content-Type"] = new[] { "text/plain" };
  12.                 return response.WriteAsync(Encoding.UTF8.GetBytes(text), 0, text.Length);
  13.             })));
  14.         }
  15.     }

使用自定義Host(self-host)託管Katana-based應用程序

使用自定義Host託管Katana應用程序與使用IIS託管差異不大,你可使用控制檯、WinForm、WPF等實現託管,但要記住,這會失去IIS帶有的一些功能(SSL、Event Log、Diagnostics、Management…),固然這能夠本身來實現。

  • 建立控制檯應用程序
  • Install-Package Microsoft.Owin.SelfHost
  • 在Main方法中使用Startup配置項構建Pipeline並監聽端口
  1. static void Main(string[] args)
  2.         {
  3.             using (WebApp.Start<Startup>("http://localhost:10002"))
  4.             {
  5.                 System.Console.WriteLine("啓動站點:http://localhost:10002");
  6.                 System.Console.ReadLine();
  7.             }
  8.         }

使用自定義的Host將失去IIS的一些功能,固然咱們能夠本身去實現。幸運的是,Katana爲咱們默認實現了部分功能,好比Diagnostic,包含在程序集Microsoft.Owin.Diagnostic中。

  1. public void Configuration(IAppBuilder app)
  2.         {
  3.             app.UseWelcomePage("/");
  4.             app.UseErrorPage();
  5.             app.Run(context =>
  6.             {
  7.                 //將請求記錄在控制檯
  8.                 Trace.WriteLine(context.Request.Uri);
  9.                 //顯示錯誤頁
  10.                 if (context.Request.Path.ToString().Equals("/error"))
  11.                 {
  12.                     throw new Exception("拋出異常");
  13.                 }
  14.                 context.Response.ContentType = "text/plain";
  15.                 return context.Response.WriteAsync("Hello World");
  16.             });
  17.         }

在上述代碼中,當請求的路徑(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。

  1. using AppFunc = Func<IDictionary<string, object>, Task>;
  2.    public class Startup
  3.    {
  4.        public void Configuration(IAppBuilder app)
  5.        {
  6.            //使用OwinHost.exe,報錯,提示轉換失敗
  7.            app.Run(context=>context.Response.WriteAsync("Hello World"));
  8.            //使用OwinHost.exe 不報錯
  9.            //app.Use(new Func<AppFunc, AppFunc>(next => (env =>
  10.            //{
  11.            // string text = "Hello World";
  12.            // var response = env["owin.ResponseBody"] as Stream;
  13.            // var headers = env["owin.ResponseHeaders"] as IDictionary<string, string[]>;
  14.            // headers["Content-Type"] = new[] { "text/plain" };
  15.            // return response.WriteAsync(Encoding.UTF8.GetBytes(text), 0, text.Length);
  16.            //})));
  17.        }
  18.    }

報錯信息以下:

  • 使用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的建立,謝謝你們支持。

本博客爲 木宛城主原創,基於 Creative Commons Attribution 2.5 China Mainland License發佈,歡迎轉載,演繹或用於商業目的,可是必須保留本文的署名 木宛城主(包含連接)。如您有任何疑問或者受權方面的協商,請給我留言。
相關文章
相關標籤/搜索