Katana-武士刀,寓意:快、準、狠!
按照常規,我們一般編寫的 ASP.NET 應用程序會部署在 IIS 上(有點傻的描述),在 ASP.NET 應用程序中,我們會大量使用 HttpContext 對象,比如:HttpContext.Current.Request 用來獲取當前 Http 請求對象,這些對象的獲取都來自于 System.Web 程序集,而這個程序集依附于 IIS,也就是說,ASP.NET 應用程序依附于 IIS,在之前了解過 runAllManagedModulesForAllRequests,知道它是 Web.config 中配置的一個節點,具體什么意思呢?就是管理所有請求與處理模塊的一個開關,當一個請求到達 IIS,它會把這個請求所要進行的處理分配給特定的處理模塊,然后進行處理,也就是配置文件中 system.webServer 下的 modules 與 handlers 配置,或者我們可以直接在 IIS 的模塊管理界面進行配置,比如管理身份驗證的 DefaultAuthentication 模塊,對應類型為 System.Web.Security.DefaultAuthenticationModule,不經意間,你又發現 System.Web 的身影了。
我們知道,ASP.NET 應用程序與 IIS 都依賴于 .NET Framework,而 ASP.NET 應用程序所要進行的處理都來自于 IIS,這其中的關系就像“亂麻”一樣,造成更深一步的依賴,讓他們不得不捆綁“在一起”。其實說白了,ASP.NET 應用程序與 IIS 的關系,有點像白蟻與蟻后的關系,什么意思呢?白蟻是蟻后生的,并且它的一些活動都是由蟻后進行控制,沒有蟻后,也就沒有白蟻,離開蟻后,白蟻也不能進行生存,比如白蟻跑到行軍蟻的巢穴,在不被行軍蟻吃掉的情況下,能生存嗎?顯然不可以。我們理想的狀態是,既然大家都是螞蟻類,就應該和諧共處,我到你那住幾天,你來我這玩幾天,這些都應該是可以的,而這樣的前提條件是,大家需要一個規約來進行共同遵守,誰也不能越過它,否則大家都玩完,這個規約就是 Owin(Open Web Interface for .Net)。
關于 Owin 的資料網上有很多,這邊就不重復造輪子了,它其實是 Web 應用程序和服務器之間交互的一種協定,用來解耦他們之間的依賴關系,交互方法簽名為 AppFunc(應用程序委托):
AppFunc = Func<IDictionary<string, object>, Task>;
IDictionary< string, object > 是什么東西呢?你可以把它看作是 Web 應用程序和服務器直接交互處理用到的一個集合數據,首先,Owin 定義了 Web 應用程序和服務器之間的一個管道(pipeline),而這個集合對象就是這個管道之中進行傳輸的數據,我們知道 Web 應用程序的處理其實就是對請求的進行處理,服務器的作用也就是對請求進行各種各樣的處理,如果我們可以把請求數據進行“規范化”,那不同的服務器都可以進行處理同樣 Web 應用程序的請求,這就是 Owin 最基本的體現,這個基礎請求數據就是(Key/Value):
- owin.RequestBody:一個帶有請求正文(如果有)的流。如果沒有請求正文,Stream.Null 可以用作占位符。
- owin.RequestHeaders:請求標頭的 IDictionary< string, string[] >。
- owin.RequestMethod:一個包含請求的 HTTP 請求方法的字符串(例如 GET 和 POST)。
- owin.RequestPath:一個包含請求路徑的字符串。 此路徑必須是應用程序委托的“根”的相對路徑。
- owin.RequestPathBase:一個字符串,包含對應于應用程序委托的“根”的請求路徑部分。
- owin.RequestProtocol:一個包含協議名稱和版本的字符串(例如 HTTP/1.0 或 HTTP/1.1)。
- owin.RequestQueryString:一個字符串,包含 HTTP 請求 URI 的查詢字符串組成部分,不帶“?”(例如 foo=bar&baz=quux),該值可以是空字符串。
- owin.RequestScheme:一個字符串,包含用于請求的 URI 方案(例如 HTTP 或 HTTPS)。
以上是 Request Data 的鍵/值說明,并不是全部,只是最基本的,全部聲明請查看:http://owin.org/spec/spec/owin-1.0.0.html。
看到這,你會不會這樣想:既然協議是公開的,如果我按照這種協議,是不是也可以自己實現一個 Web 服務器?答案是當然可以,Jesse Liu 就曾搞過:一不小心寫了個WEB服務器,但如果是從頭到尾自己搭建一個 Web 服務器,還是有些難度的。上面說了一大堆 Owin 的東西,而且是“虛”的東西,因為它只是定義,并不包含任何實現,而 Katana 是微軟基于 Owin 協議實現的一個組件和框架集合,是實實在在的東西,在說 Katana 之前,需要先說明一下 Katana 的“歷史”,因為你會發現,在新建的 ASP.NET 5 項目中,沒有了它的“身影”,這是怎么回事呢?
Owin 和 Katana 是 ASP.NET 5 之前出來的東西,以前的 Katana 源碼地址是:http://katanaproject.codeplex.com/,最近一次的代碼提交時間是 2014/8/20,可以看出,這個 Katana 代碼庫微軟現在基本上已經不維護了,那它現在跑哪里去了?首先,以前我們在 ASP.NET 應用程序中使用 Katana,需要引入的程序集是 Microsoft.Owin.,而在 ASP.NET 5 應用程序中,變成了 Microsoft.AspNet.,并且版本已初始化為 v1.0.0.,最重要的是,代碼托管也已經被拆分了。
Old Katana Source:
New 'Katana' Source:
- https://github.com/aspnet/HttpAbstractions
- https://github.com/aspnet/Hosting
- https://github.com/aspnet/Security
- https://github.com/aspnet/StaticFiles
- ...
其實你會發現,在 ASP.NET 5 中,已經沒有了 Katana 的“身影”,甚至你也很難發現 Owin 這個單詞了,為什么會這樣?微軟為什么要“模糊”它們,首先,對于 Katana 這個名詞,我覺得對于微軟來說它現在更加像一個“代號”,或者稱之為一種“象征”,代表著 ASP.NET 應用程序與 IIS 之間糾纏的結束,而不是具體實質化的東西,這也就是為什么之前程序集命名是 Microsoft.Owin.,而不是 Katana.,而對于 Owin 來說,協議是微軟自己定義的,在之前的 ASP.NET 應用程序中,你如果使用 Owin,其實是有些雞肋的,因為 ASP.NET 并沒有和 IIS 進行很好的斬斷關系,雖然項目中有個 Startup.cs 文件,但你會發現,還有一個 Web.config 文件,而 ASP.NET 5 是“進化完全版”的 Owin,其實你會發現,你很難定義 ASP.NET 5 到底是什么了?你可以把它看作是 Owin,也可以看做是 ASP.NET 應用程序,又或者是一個 Web 服務器,這些都是有可能的,因為它是一個組件型的 ASP.NET,充滿著無限可能性,這才是新一代的 ASP.NET-ASP.NET 5。
我們來看一張圖:
上面是 Katana 的項目體系結構圖,分別來看一下:
- Host-主機:運行應用程序的進程,可以是從 IIS 或獨立可執行文件到您自己的自定義程序的任何內容。主機負責啟動、加載其他 OWIN 組件和正常關閉,主機一般可以看作是服務器的操作系統提供的進程。
- Server-服務器:負責綁定到 TCP 端口,構造環境字典和通過 OWIN 管道處理請求,可以看作是管理并處理請求的一個服務器。
- Middleware-中間件:這是為處理 OWIN 管道中的請求的所有組件指定的名稱。 它可以是從簡單壓縮組件到 ASP.NET Web API 這樣的完整框架,不過從服務器的角度而言,它只是一個公開應用程序委托的組件,對,它就是組件的概念,并且不止一個。
- Application-應用程序:這是你的代碼。 由于 Katana 并不取代 ASP.NET,而是一種編寫和托管組件的新方式,因此現有的 ASP.NET Web API 和 SignalR 應用程序將保持不變,因為這些框架可以參與 OWIN 管道。事實上,對于這些類型的應用程序,Katana 組件只需使用一個小的配置類即可。
我們再來看一個更詳細的:
我們再來看下 ASP.NET 5 的 project.json 配置文件:
"dependencies": {
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-*",
"Microsoft.AspNet.Security.Cookies": "1.0.0-*",
"Microsoft.AspNet.Security.Facebook": "1.0.0-*",
"Microsoft.AspNet.Security.Google": "1.0.0-*",
"Microsoft.AspNet.Security.MicrosoftAccount": "1.0.0-*",
"Microsoft.AspNet.Security.Twitter": "1.0.0-*",
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
"EntityFramework.SqlServer": "7.0.0-*",
/*For Mono*/
"EntityFramework.InMemory": "7.0.0-*",
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
"Microsoft.Framework.OptionsModel": "1.0.0-*",
"Microsoft.AspNet.SignalR.Server": "3.0.0-*",
"Microsoft.Framework.Cache.Memory": "1.0.0-*"
},
上面是微軟 MusicStore dependencies 的部分配置,注意 Microsoft.AspNet 之前的名字叫 Microsoft.Owin,從 Hosts 開始說起,在配置信息中,我們并沒有發現它的身影,因為我們已經配置了 Microsoft.AspNet.Server.IIS,指定 IIS 來管理 Owin 組件的啟動、加載等配置,我們也可以 SelfHost,ASP.NET 5 Hosting 的源碼地址:https://github.com/aspnet/Hosting,它的作用是什么呢?從項目說明上就可以看出:Code for hosting and starting up an ASP.NET application. 再來看 Server,其實就是我們配置文件中的 Microsoft.AspNet.Server.WebListener,負責監聽并處理分發請求,而 Middleware 中間件就是 Microsoft.AspNet.Security、Microsoft.AspNet.StaticFiles 之類的處理模塊,可以有很多,但其實常規的模塊就那幾個,Application 就是 Microsoft.AspNet.Mvc,當然也可以是 WebAPI 等其他應用程序,需要的話,直接添加對應的模塊就可以了,這種分析過程來看,其實 ASP.NET 5 又可以看作是一個容器,它可以是一個 Web 應用程序,又可以是一個 Web 服務器,想怎么變就怎么變,看我七十二變?
最后,再來說下 Startup.cs 中的兩個重要方法:
// This method gets called by the runtime.
public void ConfigureServices(IServiceCollection services)
{
// Add EF services to the services container.
services.AddEntityFramework(Configuration)
.AddSqlServer()
.AddDbContext<ApplicationDbContext>();
// Add Identity services to the services container.
services.AddDefaultIdentity<ApplicationDbContext, ApplicationUser, IdentityRole>(Configuration);
// Add MVC services to the services container.
services.AddMvc();
// Uncomment the following line to add Web API servcies which makes it easier to port Web API 2 controllers.
// You need to add Microsoft.AspNet.Mvc.WebApiCompatShim package to project.json
// services.AddWebApiConventions();
}
// Configure is called after ConfigureServices is called.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
{
// Configure the HTTP request pipeline.
// Add the console logger.
loggerfactory.AddConsole();
// Add the following to the request pipeline only in development environment.
if (string.Equals(env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
{
app.UseBrowserLink();
app.UseErrorPage(ErrorPageOptions.ShowAll);
app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll);
}
else
{
// Add Error handling middleware which catches all application specific errors and
// send the request to the following path or controller action.
app.UseErrorHandler("/Home/Error");
}
// Add static files to the request pipeline.
app.UseStaticFiles();
// Add MVC to the request pipeline.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
// Uncomment the following line to add a route for porting Web API 2 controllers.
// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
});
}
ASP.NET 5 示例項目中的代碼注釋很好,對于菜鳥的我們來說,理解他們有很大的幫助,可以總結為:
- ConfigureServices:This method gets called by the runtime.
- Configure:Configure is called after ConfigureServices is called.
ConfigureServices 在運行時的時候被運行,Configure 運行在 ConfigureServices 之后,查看 ConfigureServices 中的 Add 方法注釋,你會發現最后一個單詞總是 container(容器),這是怎么回事呢,在之前學習 ASP.NET Identity 的時候,通過 AddDefaultIdentity 查看其源碼,其實就是往 IoC 容器中注入類型依賴的對象,這些類型對象的管理都是在 Owin 管道中的,你只需要在 ConfigureServices 中使用 Add 方法注冊相應模塊就可以了,其他的東西 ASP.NET 5 會幫你完成,而 Configure 是什么作用呢?我自己覺得它是配置模塊的一個“配置”,用戶你使用中間件或者應用程序的一個配置,比如,你使用 app.UseCookieAuthentication 進行配置用戶驗證的一些操作,你查看 UseCookieAuthentication 的定義,會發現其命名空間為 Microsoft.AspNet.Builder.CookieAuthenticationExtensions,所在程序集為 CookieAuthenticationExtensions(Owin 中間件),查看 Configure 中其他 Use 使用,你同樣會發現命名空間都是 Microsoft.AspNet.Builder 開頭,之前說 Owin 是一種協定,Extensions 就是一種中間件和應用程序的擴展,但都必須符合此協定,這樣才會有無限可能。
腦子油水用光了,就寫到這。
Owin 協議的實現項目:
Owin、Katana 參考資料:
文章列表