创建自定义的Middleware中间件

经过前2篇文章的介绍,相信大家已经对OWIN和Katana有了基本的了解,那么这篇文章我将继续OWIN和Katana之旅——创建自定义的Middleware中间件。

何为Middleware中间件

Middleware中间件从功能上可以理解为用来处理Http请求,当Server将Http请求封装成符合OWIN规范的字典后,交由Middleware去处理,一般情况下,Pipeline中的Middleware以链式的形式处理Http请求,即每一个Middleware都是最小的模块化,彼此独立、高效。

从语法上理解Middleware的话,他是一个应用程序委托(Func<IDictionary<stringobject>Task>)的实例,通过使用IAppBuilder 接口的Use或者Run方法将一个Middleware插入到Pipeline中,不同的是使用Run方法不需要引用下一个Middleware,即他是Pipeline中最后的处理元素。

使用Inline方式注册Middleware

使用Use方法可以将一个Middleware插入到Pipeline中,值得注意的是需要传入下一个Middleware的引用,代码如下所示:

  1. app.Use(new Func<Func<IDictionary<string, object>, Task>/*Next*/,
  2.              Func<IDictionary<string, object>/*Environment Dictionary*/, Task>>(next => async env =>
  3.              {
  4.                  string before = "Middleware1--Before(inline)"+Environment.NewLine;
  5.                  string after = "Middleware1--After(inline)"+Environment.NewLine;
  6.                  var response = env["owin.ResponseBody"] as Stream;
  7.                  await response.WriteAsync(Encoding.UTF8.GetBytes(before), 0, before.Length);
  8.                  await next.Invoke(env);
  9.                  await response.WriteAsync(Encoding.UTF8.GetBytes(after), 0, after.Length);
  10.          }));

上述代码中,实例化了一个委托,它需要传入下一个Pipeline中的Middleware引用同时返回一个新的Middleware并插入到Pipeline中。因为是异步的,所以别忘了async、await关键字。

使用Inline+ AppFunc方式注册Middleware

为了简化书写,我为应用程序委托(Func<IDictionary<stringobject>Task>)类型创建了别名AppFunc:

  1. using AppFunc=Func<IDictionary<string,object>/*Environment Dictionary*/,Task/*Task*/>;

所以又可以使用如下方式来讲Middleware添加到Pipeline中:

  1. app.Use(new Func<AppFunc, AppFunc>(next => async env =>
  2. {
  3.     string before = "\tMiddleware2--Before(inline+AppFunc)" + Environment.NewLine;
  4.     string after = "\tMiddleware2--After(inline+AppFunc)" + Environment.NewLine;
  5.     var response = env["owin.ResponseBody"] as Stream;
  6.     await response.WriteAsync(Encoding.UTF8.GetBytes(before), 0, before.Length);
  7.     await next.Invoke(env);
  8.     await response.WriteAsync(Encoding.UTF8.GetBytes(after), 0, after.Length);
  9. }));

考虑到业务逻辑的增长,有必要将Lambda表达式中的处理逻辑给分离开来,所以对上述代码稍作修改,提取到一个名为Invoke的方法内:

  1. app.Use(new Func<AppFunc, AppFunc>(next => env => Invoke(next, env)));
  2. private async Task Invoke(Func<IDictionary<string, object>, Task> next, IDictionary<string,object> env)
  3.         {
  4.             var response = env["owin.ResponseBody"] as Stream;
  5.             string pre = "\t\tMiddleware 3 - Before (inline+AppFunc+Invoke)" + Environment.NewLine;
  6.             string post = "\t\tMiddleware 3 - After (inline+AppFunc+Invoke)" + Environment.NewLine;
  7.             await response.WriteAsync(Encoding.UTF8.GetBytes(pre), 0, pre.Length);
  8.             await next.Invoke(env);
  9.             await response.WriteAsync(Encoding.UTF8.GetBytes(post), 0, post.Length);
  10.         }

虽然将业务逻辑抽取到一个方法中,但Inline这种模式对于复杂的Middleware还是显得不够简洁、易懂。我们更倾向于创建一个单独的类来表示。

定义原生Middleware类的形式来注册Middleware

如果你只想简单的跟踪一下请求,使用Inline也是可行的,但对于复杂的Middleware,我倾向于创建一个单独的类,如下所示:

  1. public class RawMiddleware
  2.   {
  3.       private readonly AppFunc _next;
  4.       public RawMiddleware(AppFunc next)
  5.       {
  6.           this._next = next;
  7.       }
  8.       public async Task Invoke(IDictionary<string,object> env )
  9.       {
  10.           var response = env["owin.ResponseBody"] as Stream;
  11.           string pre = "\t\t\tMiddleware 4 - Before (RawMiddleware)" + Environment.NewLine;
  12.           string post = "\t\t\tMiddleware 4 - After (RawMiddleware)\r\n" + Environment.NewLine;
  13.           await response.WriteAsync(Encoding.UTF8.GetBytes(pre), 0, pre.Length);
  14.           await _next.Invoke(env);
  15.           await response.WriteAsync(Encoding.UTF8.GetBytes(post), 0, post.Length);
  16.       }
  17.   }

最后,依旧是通过Use方法来将Middleware添加到Pipeline中:

  1. //两者方式皆可
  2. //app.Use<RawMiddleware>();
  3. app.Use(typeof (RawMiddleware));

上述代码中,IAppBuilder实例的Use方法添加Middleware至Pipeline与Inline方式有很大不同,它接受一个Type而非Lambda表达式。在这种情形下,创建了一个Middleware类型的实例,并将Pipeline中下一个Middleware传递到构造函数中,最后当Middleware被执行时调用Invoke方法。

注意Middleware是基于约定的形式定义的,需要满足如下条件:

  • 构造函数的第一个参数必须是Pipeline中下一个Middleware
  • 必须包含一个Invoke方法,它接收Owin环境字典,并返回Task

使用Katana Helper来注册Middleware

程序集Microsoft.Owin包含了Katana为我们提供的Helper,通过他,可以简化我们的开发,比如IOwinContext封装了Owin的环境字典,强类型对象可以通过属性的形式获取相关数据,同时为IAppBuilder提供了丰富的扩展方法来简化Middleware的注册,如下所示:

  1. app.Use(async (context, next) =>
  2.            {
  3.                await context.Response.WriteAsync("\t\t\t\tMiddleware 5--Befone(inline+katana helper)"+Environment.NewLine);
  4.                await next();
  5.                await context.Response.WriteAsync("\t\t\t\tMiddleware 5--After(inline+katana helper)"+Environment.NewLine);
  6.            });

当然我们也可以定义一个Middleware类并继承OwinMiddleware,如下所示:

  1. public class MyMiddleware : OwinMiddleware
  2.    {
  3.        public MyMiddleware(OwinMiddleware next)
  4.            : base(next)
  5.        {
  6.  
  7.        }
  8.        public override async Task Invoke(IOwinContext context)
  9.        {
  10.            await context.Response.WriteAsync("\t\t\t\t\tMiddleware 6 - Before (Katana helped middleware class)"+Environment.NewLine);
  11.            await this.Next.Invoke(context);
  12.            await context.Response.WriteAsync("\t\t\t\t\tMiddleware 6 - After (Katana helped middleware class)"+Environment.NewLine);
  13.        }
  14.    }

然后将其添加到Pipeline中:

  1. app.Use<MyMiddleware>();

Middleware的执行顺序

在完成上面Middleware注册之后,在Configuration方法的最后添加最后一个的Middleware中间件,注意它并不需要对下一个Middleware的引用了,我们可以使用Run方法来完成注册:

  1. app.Run(context => context.Response.WriteAsync("\t\t\t\t\t\tHello World"+Environment.NewLine));

值得注意的是,Pipeline中Middleware处理Http Request顺序同注册顺序保持一致,即和Configuration方法中书写的顺序保持一致,Response顺序则正好相反,如下图所示:

最后,运行程序,查看具体的输出结果是否和我们分析的保持一致:

小结

在这篇文章中,我为大家讲解了自定义Middleware的创建,Katana为我们提供了非常多的方式来创建和注册Middleware,在下一篇文章中,我将继续OWIN和Katana之旅,探索Katana和其他Web Framework的集成。

本博客为木宛城主原创,基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名木宛城主(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。

创建自定义的Middleware中间件的更多相关文章

  1. ASP.NET MVC随想录——创建自定义的Middleware中间件

    经过前2篇文章的介绍,相信大家已经对OWIN和Katana有了基本的了解,那么这篇文章我将继续OWIN和Katana之旅——创建自定义的Middleware中间件. 何为Middleware中间件 M ...

  2. Django中Middleware中间件

    Django中Middleware中间件 1 Middleware中间件概述 django中间middleware实质就是一个类,django会根据自己的规则在合适的时机执行中间件相应的方法.实际上当 ...

  3. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...

  4. Startup 和 Middleware(中间件)

    Startup 和 Middleware(中间件) ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Con ...

  5. 在ASP.NET Core中创建自定义端点可视化图

    在上篇文章中,我为构建自定义端点可视化图奠定了基础,正如我在第一篇文章中展示的那样.该图显示了端点路由的不同部分:文字值,参数,动词约束和产生结果的端点: 在本文中,我将展示如何通过创建一个自定义的D ...

  6. 带你走近AngularJS - 创建自定义指令

    带你走近AngularJS系列: 带你走近AngularJS - 基本功能介绍 带你走近AngularJS - 体验指令实例 带你走近AngularJS - 创建自定义指令 ------------- ...

  7. [转]maven创建自定义的archetype

    创建自己的archetype一般有两种方式,比较简单的就是create from project 1.首先使用eclipse创建一个新的maven project,然后把配置好的一些公用的东西放到相应 ...

  8. ArcGIS Engine环境下创建自定义的ArcToolbox Geoprocessing工具

    在上一篇日志中介绍了自己通过几何的方法合并断开的线要素的ArcGIS插件式的应用程序.但是后来考虑到插件式的程序的配置和使用比较繁琐,也没有比较好的错误处理机制,于是我就把之前的程序封装成一个类似于A ...

  9. Dockerfile创建自定义Docker镜像以及CMD与ENTRYPOINT指令的比较

    1.概述 创建Docker镜像的方式有三种 docker commit命令:由容器生成镜像: Dockerfile文件+docker build命令: 从本地文件系统导入:OpenVZ的模板. 关于这 ...

随机推荐

  1. java序列化是什么和反序列化和hadoop序列化

    1.什么是序列化和系列化DE- 神马是序列化它,序列化是内存中的对象状态信息,兑换字节序列以便于存储(持久化)和网络传输.(网络传输和硬盘持久化,你没有一定的手段来进行辨别这些字节序列是什么东西,有什 ...

  2. FTP文件操作之获取文件列表

    前面已经介绍了很多关于FTP对文件的操作,今天再跟大家介绍一个获取文件列表的功能.这个功能应该算是最简单的一个了,它只是获取了一下文件信息,而没有进行实质上的数据传输. 下面是是该功能的核心代码:   ...

  3. Android网络图片显示在ImageView 上面

    在写这篇博文的时候,我參与了一个项目的开发,里面涉及了非常多网络调用相关的问题,我记得我在刚刚開始做android项目的时候,以前就遇到这个问题,当时在网上搜索了一下,发现了一篇博文,如今与大家分享一 ...

  4. [LeetCode203]Remove Linked List Elements

    题目: Remove all elements from a linked list of integers that have value val. ExampleGiven: 1 --> 2 ...

  5. HDU 1232:流问题(并检查集合)

    pid=1232">畅通project Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  6. 纯 Swift 封装的 SQLite 框架:SQLite.swift

    SQLite.swift 是一个使用纯 Swift 语言封装 SQLite3 的操作框架. 特性: 简单的查询和参数绑定接口 安全.自动类型数据访问 隐式提交和回滚接口 开发者友好的错误处理和调试 文 ...

  7. 打印出所有&quot;水仙花数

    时间限制: 1 Sec  内存限制: 128 MB 提交: 695  解决: 352 [提交][状态][讨论版] 题目描述 打印出所有"水仙花数",所谓"水仙花数&quo ...

  8. 华为-on演习--身高找到最好的二人

    称号: 从5个人选择2作为个人礼仪器.中的每个个体的身高的范围160-190,要求2各高度差值至少(假设差异值同样的事情,他们中最高的选择),输出的两个人的身高升序. Smple input:161  ...

  9. bsh for android : 北京

    beanshell : bjtime.bsh source("/sdcard/com.googlecode.bshforandroid/extras/bsh/android.bsh" ...

  10. React.js入门笔记 创建hello world 的6种方式

    一.ReactJS简介 React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站. ...