【ASP.NET Core】給中間件傳參數的方法

最近博客更新頻率慢了些,緣由有三:數組

其一,最近老周每星期六都錄 ASP.NET Core 的直播,有些內容在視頻裏講過,就不太想在博客裏面重復。有興趣的話能夠去老周的微博看,或者去一直播,直播賬號與微博賬號是綁定的;瀏覽器

其二,最近老周是有些忙,但不是忙寫代碼的事情。而是忙着「尋寶藏」。服務器

其三,每一個星期至少也要抽出一天的時間,跟妹子出去浪。準確地說,應該叫對象(Object),是經過構造函數認識的,已經順利運行有五個月了。目前狀態良好,內存佔用小,不燒 CPU。性能好,不吃硬件。app

 

好了,屁話很少說了。今天說說如何向中間件傳參數的事。框架

首先,用宇宙中最通俗的語言介紹一下啥是中間件。瀏覽器或客戶端向服務器發出請求後,HTTP 請求會進入通訊管道,而後由一個個中間件來進行處理,A 處理完了,交給 B;B 處理完了再交給 C……全部中間件就串成一條鏈,直到最後一箇中間件(HTTP 404)。處理完後就把結果返回給調用者。異步

因此說,中間件就是一個處理 HTTP 請求的「零件」,好比,你能夠定義一箇中間件,對請求中的某些數據進行解密或者驗證。或者,你能夠定義一箇中間件來添加本身定義的 HTTP 頭。async

中間件在代碼中由一個 RequestDelegate 委託來表示,該委託聲明以下。函數

delegate System.Threading.Tasks.Task RequestDelegate(Microsoft.AspNetCore.Http.HttpContext context)

HttpContext 能夠保持整個 HTTP 請求上下文中的數據與狀態,而且在調用每個中間件時,會將自身傳遞過去。返回類型爲 Task,表示支持異步等待。工具

對於代碼簡單的中間件,能夠直接 Use 擴展方法,好比這樣。性能

            app.Use(async (context, next) =>
            {
                context.Response.Headers.Add("key", "no key");
                await next();
            });

next 是指處理鏈條上的下一個中間件,若是沒特殊事情,在當前代碼處理完後應該調用下一個中間件。固然,若是沒有必要調用下一個中間件,那能夠不調用 next,這樣,整個 HTTP 通訊管道就停止了。

可是,若是中間件邏輯多,代碼多,並且又要接收參數的話,那就應該用一個類來封裝。封裝的時候必定要注意相關的約定。

中間件必須包含一個 Invoke 或 InvokeAsync 方法,方法的參數爲 HttpContext,返回類型爲 Task。

爲何要這個約定呢?你看看剛剛那個 RequestDelegate 委託。委託類型的實例是否是有個 Invoke 方法?這就對了,大概爲了方便記憶,因此代碼約定也使用了 Invoke 這名字。在運行的時候,框架會在中間件類中尋找名字爲 Invoke 或 InvokeAsync 的方法。因此,要想自定義的中間件類起做用,你應該遵照這個約定。

咱們今天討論的重點是向中間件傳參數,要能傳參的話,就必須寫一箇中間件類。先說說,怎麼傳參。方法有二。

1、IApplicationBuilder 的 UseMiddleware 擴展方法,此方法的參數列表中,最後一個是加了 params 關鍵字的 object 數組。這個就是用來傳參數的,並且參數的個數是不肯定的。傳入的參數從中間件類的構造函數中接收。

2、經過依賴注入自動獲取。雖然中間件類的構造函數能夠接收注入對象,可是,不推薦在這裏接收注入對象,由於這樣會改變注入對象的生命週期。因爲中間件類在運行階段只實例化一次,故它的生命週期應與應用程序相等。因此,若是經過構造函數獲取注入的話,因爲注入對象長期存在引用,使得服務容器沒法釋放它。若是注入對象是用 AddTransient 方法添加到服務集合中,本應該每次使用後釋放,但因爲實例被引用,就會致使生命週期變得與應用程序同等,因此,不該該在中間件類的構造函數中來注入,而應該在 Invoke 或 InvokeAsync 方法中進行注入。

 

來來來,動手,我們用實例來學習,效果會番十倍的。

先寫一箇中間件類。

    public class DemoMiddleware
    {
        RequestDelegate _next;
        double mx, my;

        public DemoMiddleware(RequestDelegate next, double x, double y)
        {
            _next = next;
            mx = x;
            my = y;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            double r = mx + my;
            context.Response.Headers["Compute-Result"] = $"{mx} + {my} = {r}";
            await _next(context);
        }
    }

注意啊各位,中間件類的構造函數,通常都要一個 RequestDelegate 的參數,幹嘛用的呢?就是讓你能夠調用下一個中間件,它表明的是鏈條上的下一個中間件。

兩個 double 類型的參數纔是咱們真正要傳的參數。在 InvokeAsync 方法中,我作了一個簡單處理,把兩個參數的值相加,而後經過 HTTP 頭返回給客戶端。

爲了使下一個中間件能被調用,記得在 InvokeAsync 方法的最後調用一下 _next 字段。

 

如今,回到 Startup 類,找到 Configure 方法,咱們使用剛剛定義的中間件類。

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            ……

            app.UseMiddleware<DemoMiddleware>(15.33d, 8.96d);         }

參數就是經過 UseMiddleware 方法來傳遞給中間件類的,有幾個就傳幾個,該什麼類型就什麼類型,由於參數簽名是 object 類型,有容乃大,它能兼容全部類型。

 

如今咱們來測試一下。運行以後,你發現瀏覽器獲得的是 404,對的,前面老周說了,中間件鏈條的最後一箇中間件就是 404。咱們沒有向客戶端回寫任何內容,因此返回 404 太正常了。可是,代碼實際上是成功執行了的。

你能夠打開抓包工具(好比 F12 工具裏面就有),而後刷新一下,隨後你去看一下返回消息的 Http 頭。

看到了吧,說明中間件是執行了的。

 

下面,咱們用依賴注入,在中間件中獲取參數。

    public class Demo2Middleware
    {
        RequestDelegate _next;

        public Demo2Middleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context, IHostingEnvironment env)
        {
            context.Response.Headers["app-name"] = env.ApplicationName; context.Response.Headers["env-name"] = env.EnvironmentName; await _next(context);
        }
    }

在 Invoke 方法中,第一個參數是 HttpContext,這個東東是必須的,而後後面咱們獲取一個 IHostingEnvironment,這個在應用程序初始化時由框架自動添加到服務容器中,它會自動注入到 Invoke 方法中。在上面代碼中,我只是把應用名稱和運行環境名稱添加到 HTTP 頭中。

 

隨後在 Startup 類的 Configure 方法中,也用上這個中間件。

       app.UseMiddleware<Demo2Middleware>();

運行以後,刷新瀏覽器,再抓包,你就看到在 Http Header 集合中多了幾個東東。

爽吧,中間件也順利執行了。

 

其實,你仔細一看就會發現,這個 Invoke / InvokeAsync 方法的注入方式與 Startup 類的 Configure 方法是同樣的。Configure 方法的第一個參數是必須的,型類爲 IApplicationBuilder,後面的參數就是注入的。

好了,向中間件傳參數的兩種方法都介紹完了。

最後,順便說一下,「五一」假期前老周可能會直播一次 ASP.NET Core ,但不保證會開播,若是這幾天沒弄的話,就要等「五一」假期以後再開播。

相關文章
相關標籤/搜索