上一回合中,我们讲解了Linux.NET面对OWIN需要做出的准备,以及介绍了如何将两个支持OWIN协议的框架:SignalR以及NancyFX以OwinHost的方式部署到Linux.NET当中。这一章,我们将对框架与OwinHost之间怎么通过OWIN协议作出解析。
本章我们将讨论学习:
(1)、连接两世界之门——“Middleware“
(2)、转动大门的钥匙,打开无尽的财宝
(3)、适配器?转换插头
相关示例代码,可以点击这里进行下载。
一、充当”门“的”Middleware“
英文名”Middleware“,中文名”中间件“,要了解什么是Middleware,我们先看看OWIN协议中的分层。
上图为OWIN分层的一个简图。最下的一层是我们的操作系统,Linux、Windows、Unix、Mac或其他;对上一层则是运行于操作系统中的OwinHost;再往上一层也就是紫色那层是基于OWIN协议建立的基础框架;而最顶层则是我们基于这些OWIN协议的框架所诞生的应用程序(直接操作OWIN字典的暂不记录在图中)。
抛开最顶和最底两层不管,当用户从客户端发起一请求,经过漫长的网络,到达目标主机时,请求将被并且仅能被OwinHost捕获,因为只有OwinHost在持续的不断监听端口。虽然请求已经被OwinHost捕获,但是OwinHost并没有能力对这个刚捕获的请求做出处理(这里特指需要经过OWIN框架及相关应用程序处理的请求)即使它知道自身有请求需要处理。
同样的,我们再把目光转到OWIN框架,它是我们的”处理中枢“,它能够对我们把我们的输入通过适当的计算之后把正确的答案输出来,但是它也有一个缺点,那就是它自身没有办法收集”相关信息“,也就是它自己并不能产生出”输入“。
这就好比人的大脑与其他器官,OWIN框架是我们的”大脑“,OwinHost则是我们的”器官“,没有了大脑我们的其他器官也无法正常运行(当然咯,有点功能不需要大脑,就像有些OwinHost处理静态资源不需要经过OWIN框架一样),没有了其他“器官”的支持“大脑”也无法发挥作用甚至会死亡(没有宿主,OWIN框架也无法运行)。
因此,我们需要有相应的“神经”来连通我们的“器官”与“大脑”之间的通信。而Middleware发挥的就是这种作用,它是连接OwinHost与OWIN框架的门,OwinHost把捕获到的请求通过自身的处理后通过这扇门推送到OWIN框架中;而OWIN框架也自己对请求计算后得出的响应通过这扇门返回到OwinHost中,再由OwinHost推送到用户手上。
而事实上,Middleware作为一扇连接OwinHost与OWIN框架的门,让这两个世界得以交流以外,还发挥着另外一个作用,那就是规定了统一的信息出入口,所有的请求响应均只能够通过这扇门传递,这或者可以更方便的对一些敏感信息、恶意代码之流的数据进行拦截与过滤。
二、转动我们手中的钥匙
正如上一节中最后所讲到的一样,Middleware作为OWIN框架与OwinHost的唯一通道,这意味着无论是SignalR、NancyFX、Webapi、FubuMVC或是其他,它们所站立的起点高度都是一致的,我们只要把能握住Middleware,就相当于把握住了大门的钥匙,我们也可以做出我们自己的框架出来。这也是我在上一回合中所提到OWIN协议给我们带来的好处中的第二点:“它给鼓励了一批人把自己的想法变成现实”。
本节我们将简述如何直接操作OWIN字典,直接和OwinHost进行通信。
首先我们需要在Visual Studio中建立我们的项目,然后通过NuGet获得OWIN:
然后我们新建一个类,并以它作为我们的Middleware:
using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; namespace Demo1 { using AppFun = Func<IDictionary<string, object>, Task>; public class MyMiddleware { private readonly AppFun _env; public MyMiddleware(AppFun env) { if (env == null) throw new ArgumentNullException("OWIN环境参数为空"); this._env = env; } public Task Invoke(IDictionary<string, object> env) { var responseBody = "Linux.NET 学习手记(8) --小蝶惊鸿"; var responseBodyBytes = Encoding.UTF8.GetBytes(responseBody); ((IDictionary<string, string[]>)env["owin.ResponseHeaders"]).Add("Content-Type", new string[] { "text/html; charset=utf-8" }); ((Stream)env["owin.ResponseBody"]).Write(responseBodyBytes, 0, responseBodyBytes.Length); return this._env(env); } } }
这里要对代码进行一番的解析:
(1)、这是我们的Middleware,所有来自于OwinHost的请求数据以及OWIN框架响应的数据都要通过这个类来进行统一中转处理。
(2)、IDictionary<string, object>实则为OWIN字典,里面包含了基于OWIN协议的用于OwinHost与OWIN框架之间通信的数据信息。
(3)、程序启动时,OwinHost会先实例化这个类(实则调用Startup中的Configuration,然后实例化这个类,稍后我们会对此进行讲述),继而执行这个类的构造函数对Environment(也就是那个env)进行初始化。
(4)、每次OwinHost捕获到请求之后,会调用Invoke,OWIN字典会携带请求进入该方法,程序处理完成之后,OWIN字典则会携带OWIN框架的响应离开该方法。
创建好我们的Middleware之后,我们需要在项目的根目录新建一个名为“Startup”的类,并在此类里面创建一个名为“Configuration”的方法:
namespace Demo1 { using Owin; public class Startup { public void Configuration(IAppBuilder app) { app.Use(typeof(MyMiddleware)); } } }
这里也要为这个类作出一番解析:
OwinHost尝试驱动OWIN框架时,它会尝试寻找Startup,并激活里面的Configuration,继而激活我们自定义的Middleware。OwinHost激活我们的Middleware之后,OwinHost与OWIN框架之间就建立了连接,连接这两个世界的“门”也就打开了。
在示例代码中,Demo1为讲解如何通过简单的操作OWIN字典获取并返回我们想要输出的结果。
Demo2则是模拟ASP.NET MVC的路由功能,OwinHost激活Startup时,程序会对自身的程序集进行反射,找出所有以“Controller”结尾的类,并把里面的方法注册到路由字典中。当有来自于用户的请求,程序则会自动的拆解URL并在路由字典中判断是否存在改页面,存在则继而激活相应的方法(Action),不存在则导向到404页面。其效果如下图所示:
成功的路由导向到Home/Index 页面
访问一个不存在的地址,路由导向到一个404的小动画。
事实上,由于我们是基于OWIN协议直接操作OWIN字典所诞生的小Demo,因此我们是可以以一种无障碍的方式直接将项目发布到Linux.NET中运行。
三、充当转换插头角色的适配器
可能有细心而又爱动脑筋的读者不禁问到,关于那两个Demo(也能泛指其他所有的OWIN框架),OwinHost已经有了(Katana或者Jexus或其他),Middleware也由我们自行提供,不是OwinHost就可以与OWIN框架之间作出通信了吗?上一回合中所出现的适配器又是怎么回事?为什么没有见到Windows版的适配器?
在解析这个之前,我先上一张能够很恰当比喻适配器的图片:
这是我随手从网上找来的图片,用过它的读者很容易就能够分辨出来它是一个转换插头,它能够把各种类型的原插口转换成通用的插口。对的,没错,如果以简单的方式来理解,适配器所起的所用正式OwinHost与OWIN框架之间的转换插头,它把OwinHost中所提供的原始数据格式化成OWIN字典供OWIN框架使用。这就是简单的理解方式。
更深入的方式来理解,适配器发挥着两个重要的作用,除了简单理解中所讲述的作用以外,它还充当着让OwinHost成功驱动Startup并激活Configuration的关键。有兴趣的读者可以打开Jexus针对于OWIN框架的适配器,你会发现原来它也是通过反射寻找Startup类并激活里面的Configuration来创建Middleware并建立连接的,了解了这一点之后,我们可以通过修改适配器的源码来更改OwinHost尝试驱动OWIN框架时最先激活的类,我们可以根据自己的爱好把“Startup”这个名字改为“Breakdown”、“Sunday”或者“ILoveChina”甚至“ILoveXiaodiejinghong”,也可以把“Configuration”改为其他……总之你喜欢的。
至于为何Katana没有适配器,我这里只能说:“可能已经内置了吧”(具体还需要各位读者查看源码,我没有查看过,因此没有发言权)。
好的,两回合文章我们简单的认知了一些关于OWIN的事情,OWIN作为微软提出一套重要协议具有重大的战略意义。不多说了,我们下回再见吧。谢谢。