前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NET MVC随想录——创建自定义的Middleware中间件

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

作者头像
用户1161731
发布2018-01-11 13:44:57
8200
发布2018-01-11 13:44:57
举报
文章被收录于专栏:木宛城主木宛城主

经过前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的引用,代码如下所示:

代码语言:js
复制
 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:

代码语言:js
复制
 using AppFunc=Func<IDictionary<string,object>/*Environment Dictionary*/,Task/*Task*/>; 
 

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

代码语言:js
复制
 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的方法内:

代码语言:js
复制
 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,我倾向于创建一个单独的类,如下所示:

代码语言:js
复制
 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中:

代码语言:js
复制
 //两者方式皆可 
 
 //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的注册,如下所示:

代码语言:js
复制
 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,如下所示:

代码语言:js
复制
 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中:

代码语言:js
复制
 app.Use<MyMiddleware>(); 
 

Middleware的执行顺序

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

代码语言:js
复制
 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的集成。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-07-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 何为Middleware中间件
  • 使用Inline方式注册Middleware
  • 使用Inline+ AppFunc方式注册Middleware
  • 定义原生Middleware类的形式来注册Middleware
  • 使用Katana Helper来注册Middleware
  • Middleware的执行顺序
  • 小结
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档