几个核心对象:
- ApplicationBuilder 就是startup->Configure方法的第一个参数,请求(HttpContext) 就是由这个类来处理的
- HttpContext 这个就不解释了
- RequestDelegate 一个异步委托,委托的参数就是HttpContext,定义了一些对HttpContext的操做;能够看作是一个Action<HttpContext>(),只不过方法体内必须有异步的代码(await )
下面解释下ApplicationBuilder,这个类内部维护了一个中间件的列表,还有几个核心的方法:
- Use(Func<RequestDelegate, RequestDelegate> middleware),没错,就是咱们经常使用的那个app.User(...)方法。做用是把中间件加到中间件列表
- Build(),构建此应用程序用于处理HTTP请求的委托。就是把HttpContext传递给中间件,由中间件处理完成后将结果返回给用户
再看看网上经典的管道图:app
请求过来后,异步
- 执行中间件1的逻辑
- 调用Next()把处理后的HttpContext传递给中间件2
- 执行中间件2内的逻辑
- 调用Next()把HttpContext传递给中间件3
- 执行中间件3的逻辑
- 由于中间件3内没有next(),因此请求流转回中间件2
- 执行中间件2中next()方法后面定义的逻辑,请求流转回中间件1
- 执行中间件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