iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
Modern Web

30天的asp.net core實習生活-從入門到進階技巧系列 第 16

D-16 中介軟體 ? middleware

自定義中介軟體

昨天小光認識了路由,所以很好奇為什麼可以做到這樣的功能,然後又回想老K說明的請求流水線這時發現了Custom Middlewares這個東西,所以一大早就跟大頭請教這跟路由有甚麼關聯。

本文同步放置於此

中介軟體 Middleware

「前輩,路由跟中介軟體有甚麼關係阿。」
一大早小光就迫不及待的問大頭這個問題,然後今天大頭似乎把手上的需求都做完了,所以很悠哉的在上網,所以一聽到小光這麼說他就這樣回答他。
「哈哈哈,你眼睛蠻銳利的喔,其實老K那天在說明請求流水線時講的那些都是中介軟體的一種,只不過是dotnetcore內建的中介軟體。」
聽到大頭這樣回答,小光思考了一下就這麼說。
「如果是這樣的話,那如果我想要做每個請求都要做的事我可以自己定義一個中介軟體嗎?」
「哈哈哈,你想要自己做一個嗎,那我們就來學習一下怎麼做一個自訂義中介軟體。」

中介軟體的介紹

所以我們今天來學習一下如何自訂義一個中介軟體,但是在那之前我們再來看一次中介軟體這篇文章,這篇文章內介紹的除了請求流水線之外還有介紹關於中介軟體的處理方式,這裡請先看下圖。

MSDN 中介軟體管線 MSDN 中介軟體管線

就如同上次說明請求流水線時一樣,請求會依序通過一層層中介軟體,直到通過最後一個中介軟體後才會到達Action的處理,待Action的處理完成後才會在一層層的返還到最後才會把反應回饋給使用者,如此就是中介軟體的作用,接下來大家可以先看一下自訂義一個中介軟體這篇文章,然後下面我們會一步一步來學習如何自訂義中介軟體。

App.Use

首先是較簡易的一個方法,就是直接在StartUp內加入中介層的邏輯,加入的方式如下。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use((context, next) =>
    {
        // 處理資料
	
        // 可以針對context做處理
        context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
		
        // 丟給下一個middleware或是進入MVC的action內
        return next();
    });
}

這邊的Use可以一直串下去,第一個做完了會做下一個,如下列所示。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use((context, next) =>
    {
        context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
        return next();
    });
    
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });

    app.Use((context, next) =>
    {
        context.Response.Headers.Add("X-Xss-Protection", "1");
        return next();
    });
}

不過上述例子只是執行請求進去的行動,如果這邊中介層要處理進去跟返還使用者的資訊時要如下所示。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Middleware in. \r\n");
        await next.Invoke();
        await context.Response.WriteAsync("Middleware out. \r\n");
    });
}

App.Run

接下來要介紹的跟Use很類似,不同的地方是這個方法只會被執行一次,而且後續的動作也都不會執行,請看下列例子

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Middleware in. \r\n");
        await next.Invoke();
        await context.Response.WriteAsync("Middleware out. \r\n");
    });

    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello from 2nd delegate.\r\n");
    });

    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Middleware Second in. \r\n");
        await next.Invoke();
        await context.Response.WriteAsync("Middleware Second out. \r\n");
    });
}

所以上述例子的結果會是如下

Middleware in. 
Hello from 2nd delegate.
Middleware out.  

而第二個Use的動作會完全因為Run而被取消了。

App.Map / App.MapWhen

接下來要處理的是Map跟MapWhen,這都是會因為使用特定的Url而進入的中介層,接下來讓大家看下列的例子來說明兩個的差異。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 針對特定url
    app.Map("/blog", myApp =>
    {
        myApp.Run(async context =>
        {
             await context.Response.WriteAsync("Hello World!");
        });
    });

    // 條件成立則執行
    app.MapWhen(context => context.Request.Query.ContainsKey("blog"), myApp =>
    {
        myApp.Run(async context =>
        {
             await context.Response.WriteAsync("Hello World!");
        });
    });
}

由上述例子可以知道兩個的差別。

自訂義中介層類別

自訂義中介層類別可以分成兩個步驟,第一個是宣告並實作類別,第二個是使用該類別,接下來分別說明。

宣告及實作

宣告及實作中介層類別的例子如下

public class CustomMiddleware
{
    private readonly RequestDelegate _next;

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

    public Task Invoke(HttpContext httpContext)
    {
        // 做某些事情
        if (!CheckSomething())
        {
            // 結束不再往下走
            return;
        }
         
        // 繼續往下走
        return _next(httpContext);
    }
}

使用

使用自訂義中介層類別的方式有兩種,一種是註冊全域的中介層類別,另一種是針對特定類別掛上屬性attribute的方式。首先下列例子是註冊全域的方式。

app.UseMiddleware<CustomMiddleware>();

這樣所有的請求都會進到自訂義的中介層。然後接下來看一下如何針對特定類別掛上屬性的方式來使用中介層。

[MiddlewareFilter(typeof(ControllerMiddleware))]
public class HomeController : Controller
{
    [MiddlewareFilter(typeof(ActionMiddleware))]
    public IActionResult Index()
    {
        // ...
    }
}

上述例子中,進入此Controller的所有Action都會經過ControllerMiddleware中介層,而進入Index這個Action時會經過ActionMiddleware這個中介層。

擴充函式的方式

相信讀者們應該看很多UseXXX的使用中介層的方式,這其實只是因應dotnetcore的習慣透過extension的方式來註冊中介層,其實實際背後裡是執行下列的動作。

public static class CustomMiddlewareExtensions
{
    public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CustomMiddleware>();
    }
}

如此在使用中介層也只需要使用下列方式

app.UseCustomMiddleware();

後記

今天小光跟大頭學習到如何自訂義一個中介軟體,以及如何使用一個自訂義的中介軟體,並且認識了使用中介軟體時有甚麼注意事項。


上一篇
D-17 路由 ? Routing
下一篇
D-15 過濾器 ? filter ? attribute
系列文
30天的asp.net core實習生活-從入門到進階技巧31

尚未有邦友留言

立即登入留言