转载

快刀斩乱麻之 Katana

快刀斩乱麻之 Katana

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:

  • http://katanaproject.codeplex.com/

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

上面是 Katana 的项目体系结构图,分别来看一下:

  • Host-主机:运行应用程序的进程,可以是从 IIS 或独立可执行文件到您自己的自定义程序的任何内容。主机负责启动、加载其他 OWIN 组件和正常关闭,主机一般可以看作是服务器的操作系统提供的进程。
  • Server-服务器:负责绑定到 TCP 端口,构造环境字典和通过 OWIN 管道处理请求,可以看作是管理并处理请求的一个服务器。
  • Middleware-中间件:这是为处理 OWIN 管道中的请求的所有组件指定的名称。 它可以是从简单压缩组件到 ASP.NET Web API 这样的完整框架,不过从服务器的角度而言,它只是一个公开应用程序委托的组件,对,它就是组件的概念,并且不止一个。
  • Application-应用程序:这是你的代码。 由于 Katana 并不取代 ASP.NET,而是一种编写和托管组件的新方式,因此现有的 ASP.NET Web API 和 SignalR 应用程序将保持不变,因为这些框架可以参与 OWIN 管道。事实上,对于这些类型的应用程序,Katana 组件只需使用一个小的配置类即可。

我们再来看一个更详细的:

快刀斩乱麻之 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 协议的实现项目:

  • Katana
  • Nowin
  • Fracture

Owin、Katana 参考资料:

正文到此结束
Loading...