ASP.Net Core MVC基礎系列之中間件

上一節我們介紹瞭服務註冊和基本的管道執行流程, 並且講到瞭中間件, 這一節我們就來詳細談談中間件這個東西

講中間件, 其實就是講Startup類裡面的ConfigureServices 和Configure 這兩個方法

在程序啟動類Program 中, 我們在CreateWebHostBuilder 方法中調用瞭UseStartup方法, 裡面用泛型註入瞭 Startup 類, 那程序就會自動實例化這個類, 並且去執行它裡面的ConfigureServices 和Configure 這兩個方法. 我們就可以做很多配置操作

首先調用的第一個方法當然就是構造函數, 這裡沒有沒有, 我們不說, 然後就是ConfigureServices 方法. 這個方法上一節已經使用過瞭, 在這個方法裡面, 我們可以註冊一些服務或者我們叫自定義服務. 註冊完服務, 我們就可以通過依奈註入的方式在其它地方使用這些服務.

然後再執行的方法就是Configure 這個方法. 我們能看到Configure 方法默認的第一個參數是IApplicationBuilder 接口, 可以理解為整個程序的根, 我們通過這個接口對象, 可以精確的配置獲取啟用我們的中間件, 使我們的各種中間件組合起來, 形成一個完美的Web應用程序, 去處理我們的HTTP請求並且做出對應的響應等等.

講之前, 我們先把

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

這幾行代碼註釋掉, 待會再用, 再去解釋這個是幹嘛用的, 為什麼要加個if, 這裡先註釋掉

我們看這個代碼

app.Run(async (context) =>
{
    var msg = welcome.GetWelcomMsg();
    await context.Response.WriteAsync(msg);
});

這個app.Run方法, 可以理解為我們自己寫的一個中間件, 它什麼都不幹, 就是為響應輸出一個字符串. 其實這是新建項目默認生成的.

在真實的項目中, 我們基本上不會使用app.Run這個方法去處理我們的請求, 因為我做的事情遠遠比輸出一個字符串復雜很多.

在實際開發中, 我們通常使用的中間件都是app.Usexxxx開頭的, 才能組成一個完成的Web項目, 比如 app.UseMvc / app.UseStaticFiles等等這些方法去啟用系統自帶的中間件獲取別的第三方中間件.

我們這裡啟用個ASP.Net Core MVC自帶的彩蛋中間件吧, 代碼為:app.UseWelcomePage(); 啟用默認的MVC歡迎頁面. 我們F5執行.

我們能夠正常看到出現瞭一個頁面, 而不是簡單的一個字符串的輸出瞭

然後我們嘗試改變一下地址欄, 隨便寫個什麼, 可以發現, 我們隨便怎麼輸入地址, 都是進入到歡迎頁, 如圖 :

到這裡, 我們就可以發現, 這個"彩蛋"中間件的"優先級"還是很高的, 在ASP.Net Core MVC發現你使用瞭這個中間件的時候, 他就不會執行後面的東西瞭, 或者說不會執行後面的中間件瞭.

還有呢, 在我們使用中間件的時候, 或者說是在調用app.Usexxxx方法的時候, 一般來說, 這個方法都會接受一個對象參數, 這個參數可以對這個中間件進行一些配置, 下面我們嘗試配置一下"彩蛋"中間件, 告訴大傢中間件是怎麼配置的. 代碼是這樣的

app.UseWelcomePage(new WelcomePageOptions
{
    Path = "/welcome"
});

都能看到, 這裡是為"彩蛋"配置瞭一個地址(路徑), 現在我們F5看看效果

會發現網頁輸出是個字符串, 那我們在地址後面加上 /welcome 再看看, 可以發現能正常出現"彩蛋"瞭, 說明這個配置是有效的.

當然我的目的不是講這個"彩蛋"怎麼使用, 而是給大傢介紹中間件怎麼使用, 怎麼去配置中間件, 基本上所有的中間件都是這個使用方式和配置方式.

既然說到瞭這裡, 那我們來說說 app.Use 這個方法. 這個方法怎麼說呢, 就是它比較"底層", 因為app.Use裡面的參數類型是RequestDelegate , 也就是當前請求對象的委托, 相對比較"底層", 可以說是當前請求管道的"原型". 我們來寫一些代碼試試吧.

app.Use(next =>
{
    return async context =>
    {
        if (context.Request.Path.StartsWithSegments("/first"))
        {
             await context.Response.WriteAsync("First");
        }
        else
        {
            await next(context);
        }
    };
});

我們寫的代碼如上, 它的意思就是: 當前地址是/first 的時候, 輸出 First 這個字符串, 否則就不管, 繼續往下執行, 去執行別的中間件, 我們運行看看效果. 可以發現, 默認輸出的是 你好, .Net Core 2.2, 當我們把地址後面加上/first 的時候, 輸出的就是First, 如圖:

我們再輸入地址 /welcome 的是, 可以看到"彩蛋"正常出現瞭, 可以看到, 我們的Use方法是正常的

在實際開發過程中, 我們很少直接使用app.Use方法去處理我們的底層請求的, 這裡隻是簡單介紹瞭這個方法, 以及它能幹什麼.

知道app.Use是幹什麼的, 那就把上面的app.Use方法刪掉吧, 或者註釋掉, 我們下面的教程不會用的.

接下來, 我們幹點壞事, 我們在 app.Run 方法裡面手動報個異常出來, 代碼如下:

app.Run(async (context) =>
{
    throw new Exception();
    var msg = welcome.GetWelcomMsg();
    await context.Response.WriteAsync(msg);
});

我們執行項目, 會看到出現瞭500錯誤, Chrome瀏覽器是這樣的, 其它瀏覽器可能不一樣

但是我們不希望這樣, 我們希望我們能看到異常的詳細信息, 這時候, 我們就需要啟用一個異常中間件, 就是我們上面註釋掉的那個 iif (env.IsDevelopment()) 的代碼, 我們把它啟用起來, 那麼我們的Configure 方法裡面代碼就是這樣的, 如圖:

然後我們再F5運行項目, 就是這樣的瞭, 如圖:

我們的異常就很明顯瞭, 很好排查錯誤

至於為什麼加個if (env.IsDevelopment()), 這個代碼一看就知道意思瞭吧, 就是當前環境是開發環境的時候, 才啟用這個異常顯示中間件

因為我們隻希望在開發或者調試的時候, 才顯示異常的詳細信息, 別的環境不顯示, 這樣安全一些, 如果不加這個 if , 會在任何環境都會顯示詳細的錯誤信息, 這樣可能會給我們的Web應用帶來安全隱患.

所以, 我們再加個else , 在else裡面啟用一個別的中間件, 代碼如下:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler();
}

這樣我們不是在開發環境的時候, 發送異常就是這樣的:

不至於暴露一些敏感的信息

我們發現這個啟動異常頁面中間件的代碼寫在最上面, 那我們把它移動到Configure 方法的最後面, 會怎麼樣呢 ? 我們試試, 代碼如圖:

可以看到, 我把異常頁面中間件移動到最後面啟用瞭, 我們F5運行項目, 會發現, 瀏覽器不出先異常頁面瞭, 而是直接 500 錯誤.

可以知道, 中間件的啟用順序是有依奈關系和順序的, 不能隨便啟用的, 需要在正確的位置啟用對應的中間件, 如果順序錯瞭, 可能會導致中間件失效, 如果你在開發過程中, 啟用瞭某個中間件, 但是它卻沒效果, 可以使用該思路進行排查.

到這裡, 這一節就結束瞭, 到目前為止, 我們的Web應用功能很簡單, 就是輸出一個字符串和啟用瞭一個"彩蛋".

我們還講到瞭"環境", 比如上面的if (env.IsDevelopment()) 判斷是不是開發環境, 那麼怎麼改變這個環境, 讓我們在開發或者試運行和運營環境切換, 加載不同的配置和啟用不同的中間件

到此這篇關於ASP.Net Core MVC基礎系列之中間件的文章就介紹到這瞭。希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: