【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

相關文章
相關標籤/搜索