• 【aspnetcore】模拟中间件处理请求的管道


    几个核心对象:

    1. ApplicationBuilder 就是startup->Configure方法的第一个参数,请求(HttpContext) 就是由这个类来处理的
    2. HttpContext 这个就不解释了
    3. RequestDelegate 一个异步委托,委托的参数就是HttpContext,定义了一些对HttpContext的操作;可以看做是一个Action<HttpContext>(),只不过方法体内必须有异步的代码(await )

    下面解释下ApplicationBuilder,这个类内部维护了一个中间件的列表,还有几个核心的方法:

    1. Use(Func<RequestDelegate, RequestDelegate> middleware),没错,就是我们常用的那个app.User(...)方法。作用是把中间件加到中间件列表
    2. Build(),构建此应用程序用于处理HTTP请求的委托。就是把HttpContext传递给中间件,由中间件处理完成后将结果返回给用户

    再看看网上经典的管道图:

    请求过来后,

    1. 执行中间件1的逻辑
    2. 调用Next()把处理后的HttpContext传递给中间件2
    3. 执行中间件2内的逻辑
    4. 调用Next()把HttpContext传递给中间件3
    5. 执行中间件3的逻辑
    6. 因为中间件3内没有next(),所以请求流转回中间件2
    7. 执行中间件2中next()方法后面定义的逻辑,请求流转回中间件1
    8. 执行中间件1中next()方法后的逻辑,返回结果Response。

    下面是模拟的代码,因为使用了很多委托,比较烧脑,所以加了N多注释,不知道能不能说的清楚

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Threading.Tasks;
      4 
      5 namespace RequestPipe
      6 {
      7     class Program
      8     {
      9         static void Main(string[] args)
     10         {
     11             // 实例化一个处理程序
     12             var builder = new ApplicationBuilder();
     13 
     14             // 把中间件加入处理程序,给HttpContext的Name加点内容
     15             // 中间件一定要调用Next(),不然不会向后传递
     16             // await 不解释了,当代码执行到这句后,程序会进入next()的执行流程而不会继续执行后面的语句
     17             // 也就是说会显示 第一个开始执行,但 第一个执行结束 这句会等到 next() 运行完成后才执行
     18             builder.Use((next) => 
     19                 async (context) =>
     20                 {
     21                     Console.WriteLine("**********第一个开始执行**********");
     22                     context.Name += "First;";
     23                     await next(context);
     24                     Console.WriteLine("**********第一个结束执行**********");
     25                 }
     26             );
     27 
     28             builder.Use((next) =>
     29                 async (context) =>
     30                 {
     31                     Console.WriteLine("**********第二个开始执行**********");
     32                     context.Name += "Second;";
     33 
     34                     // 执行委托的方法的标准写法,也可以直接next(context)
     35                     await next.Invoke(context); 
     36                     Console.WriteLine("**********第二个结束执行**********");
     37                 }
     38             );
     39 
     40             // 特地用匿名函数来写一个,希望看起来稍微清晰一点
     41             builder.Use(
     42                 new Func<RequestDelegate, RequestDelegate>(
     43                     delegate (RequestDelegate next)
     44                     {
     45                         return new RequestDelegate(async delegate (HttpContext context)
     46                         {
     47                             Console.WriteLine("**********第三个开始执行**********");
     48                             context.Name += "Third;";
     49                             await next(context);
     50                             Console.WriteLine("**********第三个开始执行**********");
     51                         });
     52                     }
     53                 )
     54             );
     55 
     56             // 执行处理
     57             builder.Build();
     58 
     59             Console.ReadLine();
     60         }
     61     }
     62 
     63     public class ApplicationBuilder
     64     {
     65         // 中间件列表
     66         private List<Func<RequestDelegate, RequestDelegate>> middlewares = new List<Func<RequestDelegate, RequestDelegate>>();
     67 
     68         public void New()
     69         { }
     70 
     71         public void Build()
     72         {
     73             // 先构建一个基础的HttpContext
     74             var baseContext = new HttpContext();
     75 
     76             // 构建一个默认的委托(HttpContext的处理方法),就叫中间件0吧
     77             // 如果没有经过中间件处理,就直接输出404
     78             // 如果中间件处理成功,这里应该是输出 First;Second;Third;
     79             var baseDelegate = new RequestDelegate(async (context) => 
     80             {
     81                 context.Name = string.IsNullOrWhiteSpace(context.Name) ? "404" : context.Name;
     82                 await context.Show();
     83             });
     84 
     85             // 把中间件列表的顺序反转一下
     86             middlewares.Reverse();
     87 
     88             // 遍历中间件列表
     89             foreach (var middleware in middlewares)
     90             {
     91                 // 还记得moddleware的类型吧,传入一个RequestDelegate,返回一个RequestDelegate 93                 // 经过上面的反转,现在第一个元素应该是中间件3
     94                 // baseDelegate也就是中间件0现在作为参数传递给中间件3
     95                 // 中间件3内部通过 await next(context); 保存了对默认委托的调用
     96                 // 然后将中间件3返回
     97                 // 现在 baseDelegate = 中间件3
     98                 // 接下来进入列表的第二个元素,也就是中间件2
     99                 // 和上面的逻辑一样,中间件2保存了对中间件3的引用,然后将中间件2返回出来
    100                 // ...
    101                 // 列表遍历完成后,baseDelegate = 中间件1
    102                 baseDelegate = middleware.Invoke(baseDelegate);
    103             }
    104 
    105             // 执行中间件1
    106             // 中间件1中保存了对中间件2的引用,所以运行到await next()的时候,就会进入到中间件2
    107             // 同理中间件2会进入到中间件3,中间件3进入默认委托,也就是中间件0
    108             // 中间件0执行完成(此程序中就是打印HttpContext的Name属性)返回中间件3
    109             // 然后依次返回到中间件1,最终结束执行
    110             baseDelegate.Invoke(baseContext);
    111         }
    112 
    113         public void Use(Func<RequestDelegate, RequestDelegate> middleware)
    114         {
    115             middlewares.Add(middleware);
    116         }
    117     }
    118 
    119     public class HttpContext
    120     {
    121         public string Name { get; set; }
    122 
    123         public async Task Show()
    124         {
    125             Console.WriteLine(Name);
    126             await Task.CompletedTask;
    127         }
    128     }
    129 
    130     public delegate Task RequestDelegate(HttpContext context);133 }

    执行结果

    **********第一个开始执行**********
    **********第二个开始执行**********
    **********第三个开始执行**********
    First;Second;Third;
    **********第三个开始执行**********
    **********第二个结束执行**********
    **********第一个结束执行**********

    OK,这里的难点就是委托套委托,讲真的委托这东西确实强大,但代码读起来真的很难受,后面还是要整理下关于委托使用的文档,加深理解才行。

  • 相关阅读:
    阿里云服务器,http免费转https详细教程
    springboot系列总结(二)---springboot的常用注解
    springboot系列总结(一)---初识springboot
    java 查询当天0点0分0秒
    mysql各个版本驱动jar包下载 mysql/mysql-connector-java/5.1.22
    泰坦尼克 学习
    切片
    忽略warning 警告
    迭代 递归 循环 遍历
    标准化 归一化
  • 原文地址:https://www.cnblogs.com/diwu0510/p/10183373.html
Copyright © 2020-2023  润新知