创建自定义的Middleware中间件
创建自定义的Middleware中间件
阅读目录
经过前2篇文章的介绍,相信大家已经对OWIN和Katana有了基本的了解,那么这篇文章我将继续OWIN和Katana之旅——创建自定义的Middleware中间件。
何为Middleware中间件
Middleware中间件从功能上可以理解为用来处理Http请求,当Server将Http请求封装成符合OWIN规范的字典后,交由Middleware去处理,一般情况下,Pipeline中的Middleware以链式的形式处理Http请求,即每一个Middleware都是最小的模块化,彼此独立、高效。
从语法上理解Middleware的话,他是一个应用程序委托(Func<IDictionary<string, object>, Task>)的实例,通过使用IAppBuilder 接口的Use或者Run方法将一个Middleware插入到Pipeline中,不同的是使用Run方法不需要引用下一个Middleware,即他是Pipeline中最后的处理元素。
使用Inline方式注册Middleware
使用Use方法可以将一个Middleware插入到Pipeline中,值得注意的是需要传入下一个Middleware的引用,代码如下所示:
- app.Use(new Func<Func<IDictionary<string, object>, Task>/*Next*/,
- Func<IDictionary<string, object>/*Environment Dictionary*/, Task>>(next => async env =>
- {
- string before = "Middleware1--Before(inline)"+Environment.NewLine;
- string after = "Middleware1--After(inline)"+Environment.NewLine;
- var response = env["owin.ResponseBody"] as Stream;
- await response.WriteAsync(Encoding.UTF8.GetBytes(before), 0, before.Length);
- await next.Invoke(env);
- await response.WriteAsync(Encoding.UTF8.GetBytes(after), 0, after.Length);
- }));
上述代码中,实例化了一个委托,它需要传入下一个Pipeline中的Middleware引用同时返回一个新的Middleware并插入到Pipeline中。因为是异步的,所以别忘了async、await关键字。
使用Inline+ AppFunc方式注册Middleware
为了简化书写,我为应用程序委托(Func<IDictionary<string, object>, Task>)类型创建了别名AppFunc:
- using AppFunc=Func<IDictionary<string,object>/*Environment Dictionary*/,Task/*Task*/>;
所以又可以使用如下方式来讲Middleware添加到Pipeline中:
- app.Use(new Func<AppFunc, AppFunc>(next => async env =>
- {
- string before = "\tMiddleware2--Before(inline+AppFunc)" + Environment.NewLine;
- string after = "\tMiddleware2--After(inline+AppFunc)" + Environment.NewLine;
- var response = env["owin.ResponseBody"] as Stream;
- await response.WriteAsync(Encoding.UTF8.GetBytes(before), 0, before.Length);
- await next.Invoke(env);
- await response.WriteAsync(Encoding.UTF8.GetBytes(after), 0, after.Length);
- }));
考虑到业务逻辑的增长,有必要将Lambda表达式中的处理逻辑给分离开来,所以对上述代码稍作修改,提取到一个名为Invoke的方法内:
- app.Use(new Func<AppFunc, AppFunc>(next => env => Invoke(next, env)));
- private async Task Invoke(Func<IDictionary<string, object>, Task> next, IDictionary<string,object> env)
- {
- var response = env["owin.ResponseBody"] as Stream;
- string pre = "\t\tMiddleware 3 - Before (inline+AppFunc+Invoke)" + Environment.NewLine;
- string post = "\t\tMiddleware 3 - After (inline+AppFunc+Invoke)" + Environment.NewLine;
- await response.WriteAsync(Encoding.UTF8.GetBytes(pre), 0, pre.Length);
- await next.Invoke(env);
- await response.WriteAsync(Encoding.UTF8.GetBytes(post), 0, post.Length);
- }
虽然将业务逻辑抽取到一个方法中,但Inline这种模式对于复杂的Middleware还是显得不够简洁、易懂。我们更倾向于创建一个单独的类来表示。
定义原生Middleware类的形式来注册Middleware
如果你只想简单的跟踪一下请求,使用Inline也是可行的,但对于复杂的Middleware,我倾向于创建一个单独的类,如下所示:
- public class RawMiddleware
- {
- private readonly AppFunc _next;
- public RawMiddleware(AppFunc next)
- {
- this._next = next;
- }
- public async Task Invoke(IDictionary<string,object> env )
- {
- var response = env["owin.ResponseBody"] as Stream;
- string pre = "\t\t\tMiddleware 4 - Before (RawMiddleware)" + Environment.NewLine;
- string post = "\t\t\tMiddleware 4 - After (RawMiddleware)\r\n" + Environment.NewLine;
- await response.WriteAsync(Encoding.UTF8.GetBytes(pre), 0, pre.Length);
- await _next.Invoke(env);
- await response.WriteAsync(Encoding.UTF8.GetBytes(post), 0, post.Length);
- }
- }
最后,依旧是通过Use方法来将Middleware添加到Pipeline中:
- //两者方式皆可
- //app.Use<RawMiddleware>();
- 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的注册,如下所示:
- app.Use(async (context, next) =>
- {
- await context.Response.WriteAsync("\t\t\t\tMiddleware 5--Befone(inline+katana helper)"+Environment.NewLine);
- await next();
- await context.Response.WriteAsync("\t\t\t\tMiddleware 5--After(inline+katana helper)"+Environment.NewLine);
- });
当然我们也可以定义一个Middleware类并继承OwinMiddleware,如下所示:
- public class MyMiddleware : OwinMiddleware
- {
- public MyMiddleware(OwinMiddleware next)
- : base(next)
- {
- }
- public override async Task Invoke(IOwinContext context)
- {
- await context.Response.WriteAsync("\t\t\t\t\tMiddleware 6 - Before (Katana helped middleware class)"+Environment.NewLine);
- await this.Next.Invoke(context);
- await context.Response.WriteAsync("\t\t\t\t\tMiddleware 6 - After (Katana helped middleware class)"+Environment.NewLine);
- }
- }
然后将其添加到Pipeline中:
- app.Use<MyMiddleware>();
Middleware的执行顺序
在完成上面Middleware注册之后,在Configuration方法的最后添加最后一个的Middleware中间件,注意它并不需要对下一个Middleware的引用了,我们可以使用Run方法来完成注册:
- 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的集成。
创建自定义的Middleware中间件的更多相关文章
- ASP.NET MVC随想录——创建自定义的Middleware中间件
经过前2篇文章的介绍,相信大家已经对OWIN和Katana有了基本的了解,那么这篇文章我将继续OWIN和Katana之旅——创建自定义的Middleware中间件. 何为Middleware中间件 M ...
- Django中Middleware中间件
Django中Middleware中间件 1 Middleware中间件概述 django中间middleware实质就是一个类,django会根据自己的规则在合适的时机执行中间件相应的方法.实际上当 ...
- ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...
- Startup 和 Middleware(中间件)
Startup 和 Middleware(中间件) ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Con ...
- 在ASP.NET Core中创建自定义端点可视化图
在上篇文章中,我为构建自定义端点可视化图奠定了基础,正如我在第一篇文章中展示的那样.该图显示了端点路由的不同部分:文字值,参数,动词约束和产生结果的端点: 在本文中,我将展示如何通过创建一个自定义的D ...
- 带你走近AngularJS - 创建自定义指令
带你走近AngularJS系列: 带你走近AngularJS - 基本功能介绍 带你走近AngularJS - 体验指令实例 带你走近AngularJS - 创建自定义指令 ------------- ...
- [转]maven创建自定义的archetype
创建自己的archetype一般有两种方式,比较简单的就是create from project 1.首先使用eclipse创建一个新的maven project,然后把配置好的一些公用的东西放到相应 ...
- ArcGIS Engine环境下创建自定义的ArcToolbox Geoprocessing工具
在上一篇日志中介绍了自己通过几何的方法合并断开的线要素的ArcGIS插件式的应用程序.但是后来考虑到插件式的程序的配置和使用比较繁琐,也没有比较好的错误处理机制,于是我就把之前的程序封装成一个类似于A ...
- Dockerfile创建自定义Docker镜像以及CMD与ENTRYPOINT指令的比较
1.概述 创建Docker镜像的方式有三种 docker commit命令:由容器生成镜像: Dockerfile文件+docker build命令: 从本地文件系统导入:OpenVZ的模板. 关于这 ...
随机推荐
- 解决编译时出错提示: 'error: array must be initialized with a brace-enclosed initializer' 的错误
编译出现这个错误的原因非常简单编译的标准不相同.如果用stdc90,这个就可以直接编译通过了. 下面是代码例子: ...... ] = NULL;或者 :char cmd[256] = '\0'; . ...
- JAVA jdbc(数据库连接池)学习笔记(转)
学习内容: 1.JDBC的含义... JDBC想必学过JAVA的就不会陌生,JDBC到底是什么呢?其实就是由JAVA的一些类和接口构成的API,保存在java.sql和javax.sql..包中的一些 ...
- ftp的主动模式(port)与被动模式(PASV) (转)
FTP是仅基于TCP的服务,不支持UDP.与众不同的是FTP使用2个端口,一个数据端口和一个命令端口(也可叫做控制端口).通常来说这两个端口是21(命令端口)和20(数据端口).但FTP工作方式的不同 ...
- Directx11学习笔记【五】 基本的数学知识----向量篇
本文参考dx11龙书 Chapter1 vector algebra(向量代数) 要想学好游戏编程,扎实的数学知识是尤为重要的,下面将对dx11龙书中有关向量的数学知识做一下总结. 在数学中,几何向量 ...
- ExtJS4 根据分配不同的树形菜单在不同的角色登录后
继续我的最后.建立cookie后,带他们出去 var userName = Ext.util.Cookies.get('userName'); var userAuthority = Ext.util ...
- Memcahce(MC)系列(三)Memcached它PHP转让
由PHP转让Memcahce,首先,需要在server安装Memcache,如何安装Memcache这不是本文的重点, 大约memcache安装,谁的朋友有兴趣,请参阅这里:http://blog.c ...
- 第十三章——表和索引分区(1)——使用Range Left进行表分区
原文:第十三章--表和索引分区(1)--使用Range Left进行表分区 前言: 如果数据表的数据持续增长,并且表中的数据量已经达到数十亿甚至更多,数据的查询和操作将非常困难,面对非常庞大的表,几时 ...
- Java 内存架构
a) 执行. main()作为该程序的初始线的起点.无论由线程开始在其他线程. JVM有两个内螺纹:守护线程和非守护线程,main()它是一个非守护线程.常由JVM自己使用.java程序 ...
- android user如何打开一个版本号root才干
首先,你要确认你要打开adbd 的root 才干,或者让app 它有可能获得root 才干. (1). adbd 的root 才干 我们通常debug user 当问题的版本号, 或行为user ...
- 实例教程Unity3D单例模式(一)通经常使使用方法
unity3d教程 中的单例模式通经常使使用方法 通经常使使用方法是在相关类增加GetInstance()的静态方法,检查实例是否存在.假设存在,则返回.假设不存在.则返回一个"须要用游戏元 ...