ASP.NET MVC随想录——创建自定义的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的集成。
ASP.NET MVC随想录——创建自定义的Middleware中间件的更多相关文章
- 创建自定义的Middleware中间件
创建自定义的Middleware中间件 阅读目录 何为Middleware中间件 使用Inline方式注册Middleware 使用Inline+ AppFunc方式注册Middleware 定义原生 ...
- 在 ASP.NET MVC 中创建自定义 HtmlHelper
在ASP.NET MVC应用程序的开发中,我们常碰到类似Html.Label或Html.TextBox这样的代码,它将在网页上产生一个label或input标记.这些HtmlHelper的扩展方法有些 ...
- 在ASP.NET MVC中创建自定义模块
创建模块 module是实现了System.Web.IHttpModule接口的类.该接口定义了两个方法: Init:当模块初始化时被调用,传入的参数为HttpApplication对象,用于注册请求 ...
- ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇(转)
ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇 阅读目录 ASP.NET Identity 前世今生 建立 ASP.NET Identity 使用ASP.NET ...
- ASP.NET MVC 随想录—— 使用ASP.NET Identity实现基于声明的授权,高级篇
在这篇文章中,我将继续ASP.NET Identity 之旅,这也是ASP.NET Identity 三部曲的最后一篇.在本文中,将为大家介绍ASP.NET Identity 的高级功能,它支持声明式 ...
- ASP.NET MVC如何实现自定义验证(服务端验证+客户端验证)
ASP.NET MVC通过Model验证帮助我们很容易的实现对数据的验证,在默认的情况下,基于ValidationAttribute的声明是验证被使用,我们只需 要将相应的ValidationAttr ...
- asp.net mvc Route 使用自定义条件(constraints)禁止某ip登陆
asp.net mvc Route 使用自定义条件(constraints)禁止某ip登陆 前言 本文的目的是利用Mvc route创建一个自定义约束来控制路由跳转实现禁止ip登陆,当然例子可能不合理 ...
- ASP.NET MVC:创建 ModelBinder 自动 Trim 所有字符串
ASP.NET MVC:创建 ModelBinder 自动 Trim 所有字符串 2010-12-29 21:32 by 鹤冲天, 4289 阅读, 14 评论, 收藏, 编辑 用户输入的字符串前后的 ...
- ASP.NET MVC 5 - 创建连接字符串(Connection String)并使用SQL Server LocalDB
您创建的MovieDBContext类负责处理连接到数据库,并将Movie对象映射到数据库记录的任务中.你可能会问一个问题,如何指定它将连接到数据库? 实际上,确实没有指定要使用的数据库,Entity ...
随机推荐
- 原生JS中apply()方法的一个值得注意的用法
今天在学习vue.js的render时,遇到需要重复构造多个同类型对象的问题,在这里发现原生JS中apply()方法的一个特殊的用法: var ary = Array.apply(null, { &q ...
- 小JAVA大世界之程序建模跳蚤实验
package com.chigoe;//房子类class House { private int m;// 保存行数 private int n;// 保存列数 private int[][] a; ...
- .net配置文件读取
public static string LogPath { get { return AppSettingValue(); } } public static string AppSettingVa ...
- Java 反射工具类封装
封装了常用的反射相关方法 public class ReflectUtil { /** * 通过类路径获取Class * * @author LHY <br> * Description ...
- 【树状数组套权值线段树】bzoj1901 Zju2112 Dynamic Rankings
谁再管这玩意叫树状数组套主席树我跟谁急 明明就是树状数组的每个结点维护一棵动态开结点的权值线段树而已 好吧,其实只有一个指针,指向该结点的权值线段树的当前结点 每次查询之前,要让指针指向根结点 不同结 ...
- 批量创建SQL Server分区文件
) declare @i int set @table = 'v3_yqsd_report' begin exec('alter database '+@table+' add filegroup O ...
- Spring整合MyBatis
前言:MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.MyBatis 使用简单的XML或注解用 ...
- where和having子句的区别
having子句可以让我们筛选成组后的各种数据,having子句在查询过程中慢于聚合语句 having的用法 having子句可以让我们筛选成组后的各种数据,having子句在查询过程中慢于聚合语句( ...
- bdb log file 预设长度的性能优化
看代码随手记:log_put.c, __log_write() /* * If we're writing the first block in a log file on a filesystem ...
- SAS零散知识总结
1,变量名命名规范:以字母或者下划线开始,可包含字母.下划线.数字,且不超过32个字符: 2,INFILE用于读取外部数据文件,一般于FILENAME(和LIBNAME用户一致,但路径要精确到文件名( ...