[转]C# Owin 初探并创建中间件Middleware

原文链接:

本文是对上面两篇文章的汇总,对句式和格式做了一些调整,中间加了一些注释。本文相对于原文只是多了一些细节,原创内容不多,姑且就放到转载文章里面吧。

概念理解#

1.Owin定义#

Owin是Open Web Interface For .NET,.Net开源的web接口,听起来口气有点大,值得我们下面深入了解。

2.为什么要用Owin#

过去,IIS作为.NET开发者来说是最常用的Web Server(没有之一),源于微软产品的紧耦合关系,我们不得不将Website、Web Application、Web API等部署在IIS上,事实上在2010年前并没有什么不妥,但随着近些年来Web的发展,特别是移动互联网飞速发展,IIS作为Web Server已经暴露出他的不足了。主要体现在两个方面,ASP.NET (System.Web)紧耦合IIS,IIS紧耦合OS,这就意味着,我们的Web Framework必须部署在微软的操作系统上,难以跨平台

正是由于微软产品系紧耦合的关系,才造成跨平台上的不足,这也是被饱受诟病。所以我们需要OWIN来解耦,在面向对象的世界里,接口往往是解耦的关键,如下图所示:
image

使用OWIN,Web Framework不再依赖IIS和OS,这意味着你能使用任何你想的来替换IIS(比如:Katana或者Nowin),并且在必要时随时升级,而不是更新操作系统。当然,如果你需要的话,你可以构建自定义的宿主和Pipeline去处理Http请求。

这一切的改变都是由于OWIN的出现,他提供了明晰的规范以便我们快速灵活的去扩展Pipeline来处理Http请求,甚至可以不写任何一句代码来切换不同的Web Server,前提是这些Web Server遵循OWIN规范

3.作用#

Owin完全解耦了IIS,Owin可以在任何的服务器上使用。为什么呢?

它是web服务器的一个标准接口,借助这一接口使得web应用和服务器解耦。Owin在.Net web服务器和.Net web应用之间定义了一套标准的接口,其目的是为了实现服务器与应用之间的解耦。基于此标准,鼓励开发者开发简单、灵活的模块,从而推进.NET Web Development开源生态系统的发展。

OWIN定义了4层:
image

  • Host:主要负责应用程序的配置和启动进程,包括初始化OWIN Pipeline、运行Server。

  • Server:这是实际的Http Server,绑定套接字并监听的HTTP请求然后将Request和Response的Body、Header封装成符合OWIN规范的字典并发送到OWIN Middleware Pipeline中,最后Application为Response Data填充合适的字段输出。

  • Middleware:称之为中间件、组件,位于Server与Application之间,用来处理发送到Pipeline中的请求,这类组件可以是简单的Logger或者是复杂的Web Framework比如Web API、SignalR,只要Sever连接成功,Middleware中间件可以是任何实现应用程序委托的组件。

  • Application:这是具体的应用程序代码,可能在Web Framework之上。对于Web API、SignalR这类Web Framework中间件而言,我们仅仅是改变了他们的托管方式,而不是取代ASP.NET WEB API、SignalR原先的应用程序开发。所以该怎么开发就怎么开发,只不过我们将他们注册到OWIN Pipeline中去处理HTTP请求,成为OWIN管道的一部分,所以此处的Application即真正意义上的处理程序代码。

4.总结#

OWIN的思想就是简单、灵活——通过要求OWIN中间件只依赖AppFun类型,为开发基于OWIN的中间件提供了的最低门槛。同时,通过使用环境字典在各个中间件之间进行信息的传递,而非传统ASP.NET(System.Web)中使用HttpContext贯穿ASP.NET整个生命周期来传递

创建中间件#

1.创建项目#

创建一个空的webapi项目,导入Nuget包:

Install-Package Owin
Install-Package Microsoft.Owin
Install-Package Microsoft.Owin.Host.SystemWeb

注:也可以使用控制台项目Microsoft.Owin.Host.SystemWeb也可以替换成Microsoft.Owin.Host.HttpListener

2.创建Startup类#

owin中启动类定义为Startup,类中有一个Configuration方法,owin会自动查找此类此方法并执行。
注:关于owin启动类的更多说明参考 OWIN 启动类检测

下面是我的Startup类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Owin;
using MyTestOwin.Middleware;
using Owin;

[assembly: OwinStartup(typeof(MyTestOwin.Startup))]
namespace MyTestOwin
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
        }

    }
}

注:这个类可以不用手动编写,可以在项目右键“新建项”->左侧选择“web”分类->右侧选择“OWIN Startup类”,点击“确定”添加类。

3.创建自己的中间件Middleware#

创建自己的中间件的话,首先要继承OwinMiddleware类,它是抽象类。此类主要有1个构造函数、1个属性、1个抽象方法:

  • 构造函数OwinMiddleware:作用是初始化下一个可选的中间件。
  • 属性Next:意思是下一个中间件。
  • 抽象方法Invoke:创建自己的中间件都要重写此方法,此方法在中间件运行的时候就运行,处理个人请求。

下面我们来继承OwinMiddleware类,并且重构构造函数、重写Invoke方法。类名字为Middleware1,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Owin;

namespace MyTestOwin.Middleware
{
    public class Middleware1:OwinMiddleware
    {

        public Middleware1(OwinMiddleware next):base(next)
        { }


        public override Task Invoke(IOwinContext context)
        {
            if (context.Request.Path == new PathString("/owin1"))
            {
                var msg = "Owin1";
                var msgBytes = Encoding.UTF8.GetBytes(msg);
                context.Response.ContentType = "text/html;charset=utf-8";
                context.Response.Write(msgBytes, 0, msgBytes.Length);
                //解答者告诉Server解答已经完毕,后续Middleware不需要处理
                return Task.FromResult(0);
            }
            //如果不是要处理的路径,那么交付后续Middleware处理
            return Next.Invoke(context);
        }
    }
}

解析一下上面代码:

  • 首先,我们判断一下请求的link是否为’/owin1’。如果是,则执行我们想要的方法,并且执行完毕,后续所有的中间件以及controller等都不做执行处理。
    如果不是,则执行下一个中间件,直到找到对应的中间件为止。如果中间件最后找不到,则到controller里面找。

下面理清一下:

  • PathString是Miscrosoft.Owin下一个类,封装了URL处理的一些功能。
  • **Task.FromResult(0) **表示一个空的Task,说明该Middleware在某些情况下不再触发后续的Middleware运行—也就是”到此为止”。
  • 最后Next.Invoke(context)是一个非常标准的实现,把上下文交付下一个Middleware继续处理—相当于”交出接力棒”。
  • 这个Middleware是一个标准的解答者.它给出了”/owin1”这个问题的最终答案。

4.把中间件加入owin#

下面我们把中间件Middleware1加入到owin中,只需要在Startup类的Configuration方法中,加入下面的代码:

app.Use<Middleware1>();

5.测试#

1)为了方便测试&理解,我们再加入一个中间件Middleware2类,并且执行owin2 的path,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Owin;

namespace MyTestOwin.Middleware
{
    public class Middleware2 : OwinMiddleware
    {

        public Middleware2(OwinMiddleware next):base(next)
        { }


        public override Task Invoke(IOwinContext context)
        {
            if (context.Request.Path == new PathString("/owin2"))
            {
                var msg = "owin2";
                var msgBytes = Encoding.UTF8.GetBytes(msg);
                context.Response.ContentType = "text/html;charset=utf-8";
                context.Response.Write(msgBytes, 0, msgBytes.Length);
                //解答者告诉Server解答已经完毕,后续Middleware不需要处理
                return Task.FromResult(0);
            }
            //如果不是要处理的路径,那么交付后续Middleware处理
            return Next.Invoke(context);
        }
    }
}

2)再在Startup类中加入自定义中间件:

app.Use<Middleware2>();

整体代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Owin;
using MyTestOwin.Middleware;
using Owin;

[assembly: OwinStartup(typeof(MyTestOwin.Startup))]
namespace MyTestOwin
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            //注意:这里的顺序是与owin中间件类执行的顺序一样。
            app.Use<Middleware1>();
            app.Use<Middleware2>();
        }

    }
}

3)再写一个继续ApiController的类,请求路径为:http://xx/api/values/get

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MyTestOwin.Controllers
{
    [RoutePrefix("api/values")]
    public class ValuesController : ApiController
    {
        [Route("get")]
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }
    }
}

加上上面的所有东西,让我们更好地理解owin做了什么操作,与我们平时所写的apicontroller有什么联系。

下面,我们开始启动项目:

  • 分别打一个断点在Startup类的方法Configuration中、Middleware1的Invoke方法、Middleware2的Invoke方法。
  • 项目启动后,先执行Startup中的Configuration、再执行Middleware1、最后执行Middleware2。
  • 项目启动的路径是 http://localhost:52023/,并没有二级路径。
  • 上面都执行了中间件,中间件注册后,所有的请求都经过我们的中间件。

测试一:在浏览器上输入 http://localhost:52023/api/values/get

第一执行Middleware1、第二执行Middleware2、最后是ValuesController。返回结果是:controller的结果。

测试二:在浏览器上输入 http://localhost:52023/owin1

第一执行Middleware1,最后也是它执行。返回结果是:Owin1

测试三:在浏览器上输入http://localhost:52023/owin2

第一执行Middleware1、最后执行Middleware2。返回结果是:owin2

6.总结#

通过测试可以总结到的是:

  • 在startup类中注册的中间件顺序是中间件执行的顺序。
  • 每个请求都会执行中间件的Invoke方法,直到有请求的结果为止。
  • 从测试看出,中间件有点类似于拦截器。

关于服务器的实现#

目前,Katana 项目包括两个服务器实现:Microsoft.Owin.Host.SystemWebMicrosoft.Owin.Host.HttpListener,两者的区别参考项目 Katana 概述

Microsoft.Owin.Host.SystemWeb:如前所述,IIS 与 ASP.NET 管道同时充当主机和服务器。 因此,选择此托管选项时,IIS 都管理主机级别的问题,例如进程激活并侦听 HTTP 请求。 对于 ASP.NET Web 应用程序,然后将请求发送到 ASP.NET 管道。 Katana SystemWeb 主机注册一个 ASP.NET HttpModule 和 HttpHandler,以便在请求流经 HTTP 管道时截获请求,并通过用户指定的 OWIN 管道发送请求。

Microsoft.Owin.Host.HttpListener:正如其名称所示,此 Katana 服务器使用 .NET Framework 的 HttpListener 类打开套接字并将请求发送到开发人员指定的 OWIN 管道。 这是 Katana 自承载 API 和 OwinHost.exe的默认服务器选择。

关于Swagger的使用#

安装 swagger :

Install-Package Swashbuckle

在启动类的Configuration方法中添加以下代码:

app .EnableSwagger(c => c.SingleApiVersion("v1", "A title for your API"))
    .EnableSwaggerUi();   

更多界面汉化、JSON格式选择可参考 ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 在线文档

posted @   二次元攻城狮  阅读(900)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
  1. 1 烟花易冷 小柔Channel
  2. 2 红颜如霜 江壹纯
  3. 3 不谓侠 小桃Channel
  4. 4 小小恋歌 新坦结衣
  5. 5 神预言 袁娅维TIARAY
红颜如霜 - 江壹纯
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.
点击右上角即可分享
微信分享提示
主题色彩