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

原文地址:https://www.cnblogs.com/diwu0510/p/10183373.html