[ASP.NET Core] 如何使用 Middleware

如何使用 Middleware

以為已經摸熟了,結果實務專案上還是東卡西卡,於是重新練習一輪並筆記一下

前情提要


  • 通常會在 Startup.Configure() 內定義要加入使用的 Middleware
  • 加入 Middleware 的順序會影響執行時的先後順序
  • Middleware 內的委派 next(),代表下一個 Middleware

Use


public static IApplicationBuilder Use(
    this IApplicationBuilder app,
    Func<HttpContext, Func<Task>, Task> middleware)
{
    ...
}
  • 參數是一個 Func<HttpContext, Func<Task>, Task>
  • Func 的第一個參數是當前的 HttpContext,若需要Reqeust或Response操作可以直接對他處理
  • Func 的第二個參數是下一個 Middleware 的委派,在需要時呼叫,繼續後面的程序

1. 匿名委派

app.Use(async (context, func) =>
{
    await context.Response.WriteAsync("Hello World");
});

2. 傳入方法

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use(HelloWorld());
}

private static Func<HttpContext, Func<Task>, Task> HelloWorld()
{
    return async (context, func) =>
    {
        await context.Response.WriteAsync("Hello World");
    };
}

Map


  • 有些時後 Middleware 只想在符合特定路徑的時候生效,這時候可以使用 Map 方法
  • 第一個參數是 path,需要"/“開頭,不可用”/"結尾
  • 第二個參數是 Action<IApplicationBuilder>,這個委派內容就跟第一步的大同小異了
app.Map("/Test", builder =>
{
    builder.Use(async (context, func) =>
    {
        await context.Response.WriteAsync("Hello World");
    });
});

MapWhen


  • 當 Map 提供的資訊不足時,可用 MapWhen,多了一個 HttpConetxt 可以使用
  • 第一個參數從 PathString 換成 Func<HttpContext,bool>,為設定上增加了更多的彈性,可以從這次的請求內容中配置要加入 Middleware 的條件
app.MapWhen(context =>
{
    return context.Request.Query.Keys.Contains("Test");
}, builder =>
{
    builder.Use(async (context, func) =>
    {
        await context.Response.WriteAsync("Hello World");
    });
});

Run


  • 方法 Run 同樣傳入了一個委派,但是與 Use 不同的是他沒有 next 的委派參數,所以用 Run 的地方就代表這是一個 Middleware 了
​app.Run(async context =>
{
    await context.Response.WriteAsync("End");
});

Custom Middleware


  • 自定義自己的 Middleware Class
  • 建立一個Class
  • 必須有一個建構式,並傳入一個 RequestDelegate(也可以一併傳入其他需要注入的服務)
  • 必須有一個方法 Invoke 或是 InvokeAsync,並且有一個參數 HttpContext
  • 方法內的 next 前後的程式碼相當於 ing、ed 要做的事情,若沒有呼叫 next 則不會繼續執行後面的 Middleware
public class MyMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        Console.WriteLine("Stat");

        await _next.Invoke(context);

        Console.WriteLine("End");
    }
}

全域


1. 在 Startup.Configure 直接加入 Middleware

// startup
app.UseMiddleware<MyMiddleware>();

2. 建立一個 IApplicationBuilder 的擴充方法來加入 Middleware

public static void UseMyMiddleware(this IApplicationBuilder builder)
{
    builder.UseMiddleware<MyMiddleware>();
}

非全域


1. 使用上面提到的 Map 或 MapWhen

2. 在目標Action加上 [MiddlewareFilterAttribute]

  • 傳入的參數是 Type
  • 要注意這邊不能直接傳 Middleware進去,仍然需要按照約定的建立新的Class,由這個 Class 定義要加入的 Middleware
[HttpGet]
[MiddlewareFilter(typeof(MyMiddlewarePipeline))]
public IEnumerable<WeatherForecast> Get()
{
    ...
}

public class MyMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseMiddleware<MyMiddleware>();
    }
}

Microsoft Docs - ASP.NET Core Filter

Microsoft Docs - ASP.NET Core Middleware

Sample Code