阿杰,我来一步步教你在 ASP.NET Core Web API 项目里添加自定义中间件,不仅有代码,还有原理和最佳实践。


1. 中间件(Middleware)原理

在 ASP.NET Core 里,HTTP 请求会经过一个 中间件管道(Middleware Pipeline),每个中间件可以:

  1. 处理请求(读取/修改 Request)
  2. 调用下一个中间件await _next(context)
  3. 处理响应(读取/修改 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();