.NetCore中間件實現原理


中間件介紹

中間件是在應用程序管道處理請求和響應的一個鏈

每個組件都可以在請求處理前后做一些操作,並決定是否將請求交給下一個組件處理

如果一個中間件沒有把請求交給下一個中間件,稱之為管道短路

中間件的默認實現類在 Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder 中

中間件配置

配置中間件的方式很多,包括UseMiddlewareUseRun等等

但大部分配置方式都是擴展方法,最終調用的函數只有 Use(Func<RequestDelegate, RequestDelegate> middleware)

核心代碼

public delegate Task RequestDelegate(HttpContext context);

private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();

public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
    _components.Add(middleware);
    return this;
}

public RequestDelegate Build()
{
    RequestDelegate requestDelegate = delegate(HttpContext context)
    {
        context.Response.StatusCode = 404;
        return Task.CompletedTask;
    };
    foreach (Func<RequestDelegate, RequestDelegate> item in _components.Reverse())
    {
        requestDelegate = item(requestDelegate);
    }
    return requestDelegate;
}

這是ApplicationBuilder中的核心代碼,一眼看上去很簡單。但是這么這么多層的委托嵌套難以閱讀,我們接下來將他們拆開來看

RequestDelegate

這個委托不用多說,參數為HttpContext上下文,返回一個Task

Func<RequestDelegate, RequestDelegate>

這個就有意思了,他的入參是Next,返回值是當前要執行的委托,不要理解反了。

我們在Startup的Configure中寫入以下代碼

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
 {
     app.Use((RequestDelegate next) => async (HttpContext context) =>
             {
                 Console.WriteLine("before1");
                 await next.Invoke(context);
                 Console.WriteLine("after1");
             });

     app.Use((RequestDelegate next) => async (HttpContext context) =>
             {
                 Console.WriteLine("before2");
                 await next.Invoke(context);
                 Console.WriteLine("after2");
             });
     app.Use((RequestDelegate next) => async (HttpContext context) =>
             {
                 if (context.Request.Path == "/hello")
                 {
                     context.Response.StatusCode = 200;
                     await context.Response.WriteAsync("hello");
                     return;
                 }
                 await next.Invoke(context);
             });
 }

執行結果如下

before1
before2
hello
after2
after1

如果仍然難以看懂,那我們繼續拆分

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    Func<RequestDelegate, RequestDelegate> func1 = (RequestDelegate next) =>
    {
        return async context =>
        {
            {
                Console.WriteLine("before1");
                await next.Invoke(context);
                Console.WriteLine("after1");
            }
        };
    };

    Func<RequestDelegate, RequestDelegate> func2 = (RequestDelegate next) =>
    {
        return async context =>
        {
            {
                Console.WriteLine("before2");
                await next.Invoke(context);
                Console.WriteLine("after2");
            }
        };
    };

    Func<RequestDelegate, RequestDelegate> func3 = (RequestDelegate next) =>
    {
        return async context =>
        {
            {
                if (context.Request.Path == "/hello")
                {
                    Console.WriteLine("hello");
                    context.Response.StatusCode = 200;
                    await context.Response.WriteAsync("hello");
                    return;
                }
                await next.Invoke(context);
            }
        };
    };

    app.Use(func1);
    app.Use(func2);
    app.Use(func3);
}

接下來我們看一下中間件的構建過程,來解釋為什么會是上面的輸出結果

//Use()函數負責向_components列表中插入委托,此時_components中存在的委托順序為 func1,func2,func3

//接下來我們主要看Build()函數

//1.這是build函數中定義的最后一個短路委托,不會再向下調用
RequestDelegate requestDelegate = delegate(HttpContext context)
{
    context.Response.StatusCode = 404;
    return Task.CompletedTask;
};

//2._components.Reverse()將注入的委托順序反轉,執行循環
// 反轉后的順序變成了 func3 ,func2, func1
// 第一次1循環得到結果: requestDelegate=func3(requestDelegate)
// 第一次2循環得到結果: requestDelegate=func2(func3(requestDelegate))
// 第一次3循環得到結果: requestDelegate=func1(func2(func3(requestDelegate)))

//3. 結合我們的代碼,最終執行順序如下

requestDelegate = async (context) =>
             {
                 Console.WriteLine("before1");
                     Console.WriteLine("before2");
                         if (context.Request.Path == "/hello")
                         {
                             Console.WriteLine("hello");
                             context.Response.StatusCode = 200;
                             await context.Response.WriteAsync("hello");
                             return;
                         }
                             context.Response.StatusCode = 404;
                     Console.WriteLine("after2");
                 Console.WriteLine("after1");
             };

//4.最終將requestDelegate返回

所以在接收到請求時,中間件的處理順序就會按照我們定義的順序來執行啦,經過上面的介紹,是不是感覺So Easy吶!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM