在.Net Core的源碼中,不少地方都有中間件的地方,Kestrel Server和Asp.net Core 等都用了中間件的設計,好比在Kestrel Server中,Http協議的1.0, 1.1, 2.0分別註冊了不一樣的中間件從而致使不一樣方式的解析報文,這些要求了咱們如何設計一個優雅的中間件框架,在MSDN 上這樣描述了asp.net core的 中間件,每一箇中間件都應該git
這無疑給了中間件的設計難度,在經典模型裏,asp.net仍是停留在管道模型裏,定義了十幾個event,分別執行在不一樣的時間節點,而後在不一樣的module裏會註冊本身的行爲到事件上,在當時的觀念來看,這是一個很是好的代碼設計,面向切面編程,不一樣的module分工明細下降耦合性,可是隨之而來帶來的是臃腫全家桶設計,在當前的微服務年代,咱們須要動態的添加這些設計,好比認證受權session module。github
而如今的asp.net core 的中間件設計很是的好,能夠拿到下一個中間件的控制權,而且在下一個中間件以前或者結束作其餘的工做。若是不熟悉中間件的同窗能夠看看 msdn 的描述,這裏咱們來根據源碼本身實現一個優雅的中間件。編程
首先咱們要達成的效果是這樣的session
public class Startup { public void Configure(IApplicationBuilder app) { app.Use(async (context, next) => { // Do work that doesn't write to the Response. await next.Invoke(); // Do logging or other work that doesn't write to the Response. }); app.Run(async context => { await context.Response.WriteAsync("Hello from 2nd delegate."); }); } }
中間件的執行順序是這樣的app
從上面的用法能夠看到,咱們須要定義一個IApplicationBuilder,而後用use去註冊中間件到applicationbuild 對象,因此咱們定義一個IApplicationBuilder框架
public interface IApplicationBuilder { IApplicationBuilder Use(Func<HttpContext, Func<Task>, Task> middleware);//註冊中間件 IApplicationBuilder Build();//生成委託鏈 IApplicationBuilder Run();//調用委託鏈 }
Use的接口設計,是爲了咱們上面實現的效果,傳入一個方法指針(委託,後略),這個指針須要兩個參數,一個HttpContext,一個是下一個管道的方法指針,返回一個task對象, 如今爲了讓咱們的代碼跑起來,再定義一個HttpContext對象以下。asp.net
public class HttpContext { }
如今讓咱們去實現一個這個接口async
public delegate Task RequestDelegate(HttpContext httpContext);
class DefaultApplicationBuilder : IApplicationBuilder { public static List<Func<RequestDelegate, RequestDelegate>> _components = new (); public IApplicationBuilder Use(Func<HttpContext,Func<Task>,Task> middleware) { Func<RequestDelegate, RequestDelegate> component = next => { return context => { Func<Task> task = () => next(context); return middleware(context,task); }; }; _components.Add(component); return this; } }
如今咱們分析Use的實現,首先咱們定義了一個方法指針RequestDelegate,這個沒啥說的,而這個的設計妙處在DefaultApplicationBuilder中維護了一個 _components對象,是一個集合對象,定義了「二級」方法指針對象,這裏的二級指的是Func<Func<T>>對象,獲得一個「一級」方法指針處理後返回另外一個「一級」方法指針。如今咱們看一下這個Use方法的實現,一箇中間件middleware就至關於一個方法指針,這時候它定義了一個component,獲取一個方法指針,而後返回一個方法指針,注意在返回的方法指針裏,它將以前傳入的方法指針從新包裝了一下獲得task對象,這個至關於二級的指針,而後傳給中間件。這個地方有點繞。你們須要多看一下理解其中的含義。微服務
而後咱們再實現一下build 和run 方法以下。ui
public IApplicationBuilder Build() { RequestDelegate app = context => Task.CompletedTask; _components.Reverse(); foreach (var component in _components) { app = component(app); } requestDelegate = app; return this; } public IApplicationBuilder Run() { var context = new HttpContext(); requestDelegate(context); return this; }
簡單說一下build方法,這裏的設計之妙就在於將「二級」指針轉發成「一級」指針並生成一個委託鏈,其中的next參數裝的就是一系列的委託鏈。返回的就是第一個註冊的中間件。如今咱們使用一下這個中間件吧。
static void Main(string[] args) { IApplicationBuilder applicationBuilder = new DefaultApplicationBuilder(); applicationBuilder.Use(async (context, next) => { Console.WriteLine(1); await next.Invoke(); Console.WriteLine(2); }); applicationBuilder.Use(async (context, next) => { Console.WriteLine(3); await next.Invoke(); Console.WriteLine(4); }); applicationBuilder.Use(async (context, next) => { Console.WriteLine(5); await next.Invoke(); Console.WriteLine(6); }); applicationBuilder .Build() .Run(); Console.WriteLine("Hello World!"); }
返回結果就是以下,就是msdn文檔所說的調用邏輯。
1 3 5 6 4 2 Hello World!
這一塊理解起來比較難,設計了這中間件這一塊的人很厲害,已經將代碼上傳到github 上了,你們有興趣能夠對比代碼來研究分析。若是有任何問題歡迎你們留言。謝謝你們的閱讀