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):
以上是 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:
其实你会发现,在 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 的项目体系结构图,分别来看一下:
我们再来看一个更详细的:
我们再来看下 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 在运行时的时候被运行,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 参考资料: