阿杰,我来一步步教你在 ASP.NET Core Web API 项目里添加自定义中间件,不仅有代码,还有原理和最佳实践。
1. 中间件(Middleware)原理
在 ASP.NET Core 里,HTTP 请求会经过一个 中间件管道(Middleware Pipeline),每个中间件可以:
- 处理请求(读取/修改 Request)
- 调用下一个中间件(
await _next(context)
) - 处理响应(读取/修改 Response)
它的模式类似 洋葱模型:
请求进来时一层层“剥”,响应返回时一层层“包回去”。
2. 创建自定义中间件
2.1 最简单的实现
新建一个类:
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
public MyCustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 处理请求前
Console.WriteLine($"[Request] {context.Request.Method} {context.Request.Path}");
// 调用下一个中间件
await _next(context);
// 处理响应后
Console.WriteLine($"[Response] Status Code: {context.Response.StatusCode}");
}
}
2.2 扩展方法(让调用更优雅)
public static class MyCustomMiddlewareExtensions
{
public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyCustomMiddleware>();
}
}
3. 在管道中注册中间件
Program.cs
(.NET 6+ Minimal Hosting 模式):
var builder = WebApplication.CreateBuilder(args);
// 注册服务
builder.Services.AddControllers();
var app = builder.Build();
// 使用自定义中间件
app.UseMyCustomMiddleware();
// 使用路由和控制器
app.MapControllers();
app.Run();
📌 执行顺序很重要
中间件的顺序决定了谁先处理请求、谁先处理响应。
例如,你想做日志记录,应该放在最前面;想做异常处理,也最好放在最外层。
4. 带参数的中间件
如果中间件需要配置参数,可以用依赖注入:
public class MyOptions
{
public string Prefix { get; set; }
}
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
private readonly MyOptions _options;
public MyCustomMiddleware(RequestDelegate next, IOptions<MyOptions> options)
{
_next = next;
_options = options.Value;
}
public async Task InvokeAsync(HttpContext context)
{
Console.WriteLine($"{_options.Prefix} Request: {context.Request.Path}");
await _next(context);
}
}
注册:
builder.Services.Configure<MyOptions>(opt =>
{
opt.Prefix = "[LOG]";
});
app.UseMyCustomMiddleware();
5. 常见自定义中间件场景
- 全局异常捕获(替代 WebForms 的
Application_Error
) - 请求/响应日志
- 自定义身份认证
- 限流(Rate Limiting)
- IP 黑白名单
- 请求重写 / URL Rewrite
6. 一个全局异常处理中间件示例
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalExceptionMiddleware> _logger;
public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception");
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("{\"error\":\"Internal Server Error\"}");
}
}
}
public static class GlobalExceptionMiddlewareExtensions
{
public static IApplicationBuilder UseGlobalExceptionHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<GlobalExceptionMiddleware>();
}
}
注册:
app.UseGlobalExceptionHandler();
发表回复