以為已經摸熟了,結果實務專案上還是東卡西卡,於是重新練習一輪並筆記一下
前情提要
- 通常會在 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");
}
}
全域
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
以為已經摸熟了,結果實務專案上還是東卡西卡,於是重新練習一輪並筆記一下
前情提要
Use
public static IApplicationBuilder Use( this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> 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
app.Map("/Test", builder => { builder.Use(async (context, func) => { await context.Response.WriteAsync("Hello World"); }); });
MapWhen
app.MapWhen(context => { return context.Request.Query.Keys.Contains("Test"); }, builder => { builder.Use(async (context, func) => { await context.Response.WriteAsync("Hello World"); }); });
Run
app.Run(async context => { await context.Response.WriteAsync("End"); });
Custom 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]
[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