Fork me on GitHub

快刀斩乱麻之 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:

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 的项目体系结构图,分别来看一下:

  • 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 参考资料:

posted @ 2015-01-13 12:15  田园里的蟋蟀  阅读(8477)  评论(10编辑  收藏  举报