【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传递给中间件,由中间件处理完成后将结果返回给用户

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

请求过来后,异步

  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多注释,不知道能不能说的清楚async

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

相关文章
相关标签/搜索