# ASP.NET Core - Middleware Middleware (中介軟體)是指從發出 Request 到接收 Reponse 途中,用來處理特定用途的程式。 微軟官方使用 Pipeline 來形容這個往返的過程,如同水管可以串聯在一起,所有的 Request 和 Reponse 都會一層層的經過這些水管,也可說像是生產線上的流水線。  從上圖可以看出 Request 到最裡面的 Middleware 3 後會在沿著原路回去,經過的順序是先進後出(FILO)。 一個 Middleware 可以分成三部分 1. before logic : 指定 Request Pipeline 經過時執行的邏輯 2. next : 呼叫下一個 Middleware,也可以不呼叫 3. after logic : 指定 Reponse Pipeline 經過時執行的邏輯 ## 設定方式 預設在 Startup 的 Configure 設定,ASP.NET Core 內建了許多好用的 Middleware,如驗證、回應壓縮等等。 ## 自訂 Middleware 使用 Run、Use、Map 自訂 Middleware 1. Run : 為所有 Middleware 的最末端,可以說是最後一個行為,當碰到他之後便會開始回流(下方範例的 Middleware - last) 2. Use : 是用 Use 在 Startup 的 Configure 對 IApplicationBuilder 註冊,可以加入條件判斷再透過呼叫 next 指定執行下一層 Middleware,也可以指定回流時所要執行的動作。例如 : app.UseMVC() 3. Map : 判斷路由規則是否符合預期,可以依照不同的 URL 指向不同的 Run 及註冊不同的 Use ### App.Use 範例 : ```csharp= app.Use(async (context, next) => { await context.Response.WriteAsync("Middleware 1 - request\n"); await next.Invoke(); await context.Response.WriteAsync("Middleware 1 - reponse\n"); }); app.Use(async (context, next) => { await context.Response.WriteAsync("Middleware 2 - request\n"); await next.Invoke(); await context.Response.WriteAsync("Middleware 2 - reponse\n"); }); app.Run(async (context) => { await context.Response.WriteAsync("Middleware - last\n"); }); ``` 瀏覽器上顯示的輸出 ``` Middleware 1 - request Middleware 2 - request Middleware - last Middleware 2 - reponse Middleware 1 - reponse ``` 從上面的輸出結果可以看出註冊的順序非常重要,Pipeline 會遵循先進後出的原則執行。 #### Middleware 也可以作為攔截器,不符合條件則不繼續往下送 範例 : ```csharp= app.Use(async (context, next) => { await context.Response.WriteAsync("Middleware 1 - request\n"); await next.Invoke(); await context.Response.WriteAsync("Middleware 1 - reponse\n"); }); app.Use(async (context, next) => { await context.Response.WriteAsync("Middleware 2 - request\n"); // 條件未通過不往下送,往回流 var result = false; if (result) { await next.Invoke(); } await context.Response.WriteAsync("Middleware 2 - reponse\n"); }); app.Run(async (context) => { await context.Response.WriteAsync("Middleware - last\n"); }); ``` 輸出結果 ``` Middleware 1 - request Middleware 2 - request Middleware 2 - reponse Middleware 1 - reponse ``` ### App.Map 範例 : ```csharp= app.Use(async (context, next) => { await context.Response.WriteAsync("Middleware 1 - request\n"); await next.Invoke(); await context.Response.WriteAsync("Middleware 1 - reponse\n"); }); app.Map("/second", map => { map.Use(async (context, next) => { await context.Response.WriteAsync("Middleware 2 - request\n"); // 條件未通過不往下送,往回流 var result = false; await next.Invoke(); await context.Response.WriteAsync("Middleware 2 - reponse\n"); }); map.Run(async context => { await context.Response.WriteAsync("Map Second\n"); }); }); app.Run(async (context) => { await context.Response.WriteAsync("Middleware - last\n"); }); ``` 開啟瀏覽器輸出結果 ``` Middleware 1 - request Middleware - last Middleware 1 - reponse ``` 指定 /second (localhost:5001/second) 的輸出結果,可以看出只有對應的 URL 才會執行這層 Middleware ``` Middleware 1 - request Middleware 2 - request Map Second Middleware 2 - reponse Middleware 1 - reponse ``` ### 將 Middleware 包成類別 Middleware 必須具備 * 具有 RequestDelegate 類型參數的公用建構函式 * 函式名稱為 `Invoke` 或 `InvokeAsync` 的公用方法,此方法必須: * 傳回 Task * 傳入 HttpContext 類型的參數 建構函式和 `Invoke`/`InvokeAsync` 的其他參數會由DI所填入 範例 : ```csharp= /// <summary> /// class FirstMiddleware /// </summary> public class FirstMiddleware { private readonly RequestDelegate _next; /// <summary> /// Initializes a new instance of the <see cref="FirstMiddleware"/> class. /// </summary> /// <param name="next">The next.</param> public FirstMiddleware(RequestDelegate next) { this._next = next; } /// <summary> /// Invokes the asynchronous. /// </summary> /// <param name="context">The context.</param> public async Task InvokeAsync(HttpContext context) { await context.Response.WriteAsync("Middleware 1 - request\n"); await this._next(context); await context.Response.WriteAsync("Middleware 1 - reponse\n"); } } ``` 在 Startup 的 Configure 加入全域註冊 ```csharp= app.UseMiddleware<FirstMiddleware>(); ``` 也可以只套用在特定的 Controller 或 Action ```csharp= [MiddlewareFilter(typeof(FirstMiddleware))] public class HomeController : Controller { [MiddlewareFilter(typeof(FirstMiddleware))] public IActionResult Index() { // ... } } ``` ### 擴充 Middleware 大部分擴充的 Middleware 都會用一個靜態方法包裝,例如 : `UseMVC()`,自訂的 Middleware 同樣可以進行包裝。 針對 IApplicationBuilder 進行擴充,並填入自訂的 Middleware ```csharp= public static class CustomMiddlewareExtensions { public static IApplicationBuilder UseFirstMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<FirstMiddleware>(); } } ``` 擴充包裝完成後就可以使用擴充方法直接註冊 ```csharp= public class Startup { // ... public void Configure(IApplicationBuilder app) { app.UseFirstMiddleware(); // ... } } ``` ## 參考 [1][ASP.NET Core Middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1) [2][ASP.NET Core 2 系列 - Middleware](https://blog.johnwu.cc/article/ironman-day03-asp-net-core-middleware.html) [3][撰寫自訂的 ASP.NET Core 中介軟體](https://docs.microsoft.com/zh-tw/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1) ###### tags: `C#` `Dotnet Core` `Middleware`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up