.Net Core如何優雅的實現中間件

在.Net Core的源碼中,不少地方都有中間件的地方,Kestrel Server和Asp.net Core 等都用了中間件的設計,好比在Kestrel Server中,Http協議的1.0, 1.1, 2.0分別註冊了不一樣的中間件從而致使不一樣方式的解析報文,這些要求了咱們如何設計一個優雅的中間件框架,在MSDN 上這樣描述了asp.net core的 中間件,每一箇中間件都應該git

  • Chooses whether to pass the request to the next component in the pipeline.(選擇是否將請求傳遞到管道中的下一個組件)
  • Can perform work before and after the next component in the pipeline.(可在管道中的下一個組件先後執行工做)

這無疑給了中間件的設計難度,在經典模型裏,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 上了,你們有興趣能夠對比代碼來研究分析。若是有任何問題歡迎你們留言。謝謝你們的閱讀

相關文章
相關標籤/搜索