一、简介

在ASP.NET5里Web Server与应用之间的沟通采用OWIN(Open Web Interface for .NET);其实如果写过NodeJS就明白,这种中间件的形式在NodeJS各种WEB框架到处使用。然而从系统架构的角度来说,将ASP.NET将不再依赖于IIS,也可以用Self-Hosting模式,我们甚至可以采用一个Linux服务器来部署ASP.NET程序。

从上至下,OWIN架构共有四层:

  • Application
    Web程序。
  • Application Framework(Middleware)
    请求处理模块组,这些模块也叫中间件,请求时会根据中间件加载顺利逐一处理,只要不中断处理请求,OWIN会一直下去,直到所有的中间件都执行完毕。而这些中间件非常多,小到记录日志,大到Web API、MVC、SignalR等等。
  • Server
    构建OWIN环境和Request。
  • Host
    诸如:IIS、Katana.exe或者自己开发Web服务器。

二、OWIN有什么好处?

如果说System.Web.dll是一个什么都能干的万能框架,那么OWIN就是就像一把利刀。假如我需要提供表态文件(图片、CSS、JavaScript),导入静态文件的处理模块;假如我需要提供微信OAuth,导入一个处理模块。所有任何你想要的功能都可以像吃自助餐一样,你需要什么?加入什么!

我们写一个极为简单的应用,请求时返回一个文本格式。

using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(OWINDemo.Startup))]
namespace OWINDemo
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.Run(context =>
            {
                context.Response.ContentType = "text/plain";
                return context.Response.WriteAsync("Hello, world.");
            });
        }
    }
}

这个应用在任何请求的情况下都直接返回一个 Hello, world.,这里并没有什么Web API、登录、Cookie。然而我们并不可能只是单纯这样,我们需要路由、安全认证机制等等,只需要导入所需要的模块,所有的这一些模块就是所谓的中间件管道。

三、加载OWIN模块

1、Web API中间件

继续上面的示例,我们引入Web API中间件,让Web应用变成一个Web API应用。添加Microsoft.AspNet.WebApi.Owin,然后把Web API中间件配置信息加入到 Startup.cs 里:

var routes = new HttpRouteCollection();
routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
app.UseWebApi(new HttpConfiguration(routes));

接着,为了让Web API有个有效请求路径,还需要加下一个Contoller,代码如下:

public class ValuesController : ApiController
{
    public int Get(int v)
    {
        return v;
    }
}

当我们访问 /values/get?v=100,会得到一个 100 的数字;而如果访问默认首页依然返回 Hello, world.

2、Cors中间件

继续上面的示例,API接口跨域是一个问题,因此我们还需要让API接口支持CORS(跨域资源共享),好在M$也提供了相应的模块,添加Microsoft.Owin.Cors,然后配置信息加入到 Startup.cs 里:

app.UseCors(new CorsOptions());

好了,我们的API接口也已经支持CORS。

怎么样?是不是太酷了,Web应用变得非常的轻,需要什么加入什么。如果你需要些什么模块,建议你先到Nuget搜索:owin,也许就已经有人写好了。

四、开发Owin

那么如何开发像上面一样OWIN呢?其实上面的示例当中 Hello, world. 就是一个完全的OWIN中间件,只不过它实在太简单,以至于让我们好像忽略OWIN的存在感。

1、Lambda表达式

app.Use(async (context, next) =>
{
    context.Response.Write("Lambda 表达式");
    await next();
});

他非常适合简单业务或用于诊断的中间件。

2、Middleware 类型

创建一个类,并且必须包括构建函数第一个参数必须是处理通道的下一个待处理函数和一个 Invoke 函数接收的是OWIN环境变量。

using AppFunc = Func<IDictionary<string, object>, Task>;

public class MyMiddlewareClass
{
    private readonly AppFunc next;

    public MyMiddlewareClass(AppFunc next)
    {
        this.next = next;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        await next.Invoke(environment);
    }
}

然后在 Startup.cs 加入配置:

app.Use(typeof(MyMiddlewareClass));

OWIN会反射调用Invoke;如果你对反射比较反感,那么介绍一种实例的方式。

3、Middleware 实例

相比较第二种,只是在这基础上将构建函数更改为一个 Initialize 方法。

using AppFunc = Func<IDictionary<string, object>, Task>;

public class InstanceMiddleware
{
    private AppFunc next;

    public void Initialize(AppFunc next)
    {
        this.next = next;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        await next.Invoke(environment);
    }
}

在加载上也不一样,只需要实例一个 InstanceMiddleware 交给OWIN通道就行。

app.Use(new InstanceMiddleware());

4、OwinMiddleware

第二、三种你会发现,你无法给把信息输出到页面。那么我们可以考虑采用 OwinMiddleware;他是 Microsoft.Owin 的一个抽象类,并且提供一个 IOwinContext 用来访问OWIN环境。

public class LoggingMiddleware : OwinMiddleware
{
    public LoggingMiddleware(OwinMiddleware next)
        : base(next)
    {
    }

    public async override Task Invoke(IOwinContext context)
    {
        context.Response.Write("LoggingMiddleware");
        await Next.Invoke(context);
    }
}

配置模块:

app.Use(new LoggingMiddleware());

以上四种编写方式各有适用环境,从代码的角度看,将现有模块转化成OWIN模块成本非常低,而这些模块我们还可以在另一项目得到重用。