• 创建自定义的Middleware中间件


    创建自定义的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 = " Middleware2--Before(inline+AppFunc)" + Environment.NewLine;
    4.     string after = " Middleware2--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 = " Middleware 3 - Before (inline+AppFunc+Invoke)" + Environment.NewLine;
    6.             string post = " Middleware 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 = " Middleware 4 - Before (RawMiddleware)" + Environment.NewLine;
    12.           string post = " Middleware 4 - After (RawMiddleware) " + 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(" Middleware 5--Befone(inline+katana helper)"+Environment.NewLine);
    4.                await next();
    5.                await context.Response.WriteAsync(" Middleware 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(" Middleware 6 - Before (Katana helped middleware class)"+Environment.NewLine);
    11.            await this.Next.Invoke(context);
    12.            await context.Response.WriteAsync(" Middleware 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(" Hello 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发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名木宛城主(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。
  • 相关阅读:
    011 处理模型数据时@ModelAttribute的使用
    动态产生DataSource------待整理
    连接池问题
    maven加载第三方jar不能加载
    010 处理模型数据(ModelAndView,Map Model,@SessionAttributes)
    009 使用servlet API作为参数
    008 使用POJO对象绑定请求参数
    007 @CookieValue绑定请求中的cookie
    006 请求处理方法签名
    005 RequestMapping_HiddenHttpMethodFilter 过滤器
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4625893.html
Copyright © 2020-2023  润新知