[鐵人賽Day04] ASP.Net Core MVC 進化之路 - 淺談Middleware

在ASP.Net Core中**Middleware**的設計非常直覺跟彈性,

本篇將記錄ASP.Net Core Middleware的相關用法。

在介紹Middleware前,先看個情境題

某天早上你去咖啡廳寫程式
中午去看電影,
下午又去健身房,
走到鋼鐵三路要搭公車回家時發現悠遊卡掉了,
請問你會去哪裡找?
(假設悠遊卡就掉在這三個地方裡)

如果以筆者記性這麼差的人,

我會按照電影院 -> 健身房 -> 咖啡廳的路線順序去找。

不過假設我在健身房找到悠遊卡,那我就不會再去咖啡廳,

而由於這條路是死巷,所以我只好沿原路返回鋼鐵三路。

 

這就是Middleware路由的運作方式

Middleware中文翻譯成「中介軟體」,

是指從發出請求(Request)之後,

到接收回應(Response)這段來回的途徑上,

用來處理特定用途的程式。

MSDN上用管線(Pipeline)來形容往返的過程。

比較常見的Middleware有身份驗證(Identity)、路由(Routing)或回應壓縮(Response Compression)等。

從上圖可以很清楚的看到,

Request到最裡面(Middleware 5)後,

要再沿著原路回去(後進先出,LIFO)。

 

一個Middleware可以分成三大部分:

  • before logic:指定Request Pipeline經過時執行的邏輯。
  • next:呼叫下一個Middleware(也可以決定不呼叫)。
  • after logic:指定Request Pipeline經過時執行的邏輯。

Middleware預設在StartupConfigure設定,

.Net Core預設內建了許多好用的Middleware

如驗證(Authentication)、回應壓縮(Response Compression)、URL重寫(URL Rewriting)等,

如需要更詳細的資訊可以參考MSDN

 

我們可以使用RunUseMap自訂Middleware

簡單說明一下其中的差別。

  • Run:是所有Middleware中的最末端的行為,可以想成它是一道牆,碰到它之後Pipeline則開始回流。
  • Use:一般會使用Use進行自訂的Middleware擴充,能透過呼叫next()指定執行下一層Middleware(可加入條件判斷決定是否呼叫),也可指定在管線回流時所要執行的行為。
  • Map:主要在判斷路由規則是否符合預期,符合則執行區間內容。

以下為範例程式碼Startup

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("middleware - use test before map check -  request in\n");
        var condition = true;
        if (condition)
        {
            await next();
        }
        await context.Response.WriteAsync("middleware - use test before map check -  response out\n");
    });

    app.Map("/map1", applicationBuilder =>
    {
        applicationBuilder.Use(async (context, next) =>
        {
            await context.Response.WriteAsync("middleware -  use test in map1 - request in\n");
            await next.Invoke();
            await context.Response.WriteAsync("middleware -  use test in map1 - response out\n");
        });

        applicationBuilder.Run(async context =>
        {
            await context.Response.WriteAsync("middleware - run test in map1\n");
        });
    });

    app.Map("/map2", applicationBuilder =>
    {
        applicationBuilder.Run(async context =>
        {
            await context.Response.WriteAsync("middleware - run test only in map2\n");
        });
    });

    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("middleware - use test after map check - request in\n");
        var condition = true;
        if (condition)
        {
            await next();
        }
        await context.Response.WriteAsync("middleware - use test after map check - response out\n");
    });
            
    app.Run(async context =>
    {
        await context.Response.WriteAsync("middleware - run test in the end\n");
    });
}

接著在瀏覽器網址列測試。

輸入https://localhost:{your_port}/

輸入https://localhost:{your_port}/map1

輸入https://localhost:{your_port}/map2

補充一下,

如果使用Use指定了next()但後面卻沒有其他Middleware行為,

這樣編譯上是檢查不出來的,但執行後則會失敗。

如果有判斷是否有下一層Middleware的方式,

再麻煩各路大神提點,

Middleware的筆記就先寫到這邊。

 

參考

https://docs.microsoft.com/zh-tw/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1