幾個核心對象:
- 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