ASP.NET Core中间件开发实战:自定义请求处理管道
引言:为什么需要自定义中间件?
在ASP.NET Core开发中,你是否遇到过这样的场景:需要统一处理所有请求的日志记录、需要实现自定义的身份验证逻辑、或者需要在特定条件下重写响应内容?这些需求正是中间件(Middleware)大显身手的时刻。
ASP.NET Core的请求处理管道(Request Pipeline)是一个强大的架构,它允许开发者通过中间件组件来灵活地处理HTTP请求和响应。本文将带你深入理解中间件的工作原理,并通过实战案例教你如何开发自定义中间件。
中间件核心概念解析
什么是中间件?
中间件是ASP.NET Core请求处理管道中的组件,它们按顺序处理HTTP请求和响应。每个中间件都可以:
- 处理传入的请求
- 将请求传递给管道中的下一个中间件
- 处理传出的响应
中间件管道工作原理
中间件的三种形式
| 形式 | 描述 | 适用场景 |
|---|---|---|
| 基于类的中间件 | 完整的类实现 | 复杂逻辑、可测试性要求高 |
| 内联中间件 | 使用Use、Run、Map方法 | 简单逻辑、快速原型 |
| 基于工厂的中间件 | 实现IMiddleware接口 | 依赖注入场景 |
实战一:创建基于类的自定义中间件
步骤1:定义中间件类
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace CustomMiddleware
{
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;
public RequestLoggingMiddleware(
RequestDelegate next,
ILogger<RequestLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// 记录请求开始时间
var startTime = DateTime.UtcNow;
_logger.LogInformation("开始处理请求: {Method} {Path}",
context.Request.Method, context.Request.Path);
try
{
// 调用管道中的下一个中间件
await _next(context);
// 记录请求完成信息
var duration = DateTime.UtcNow - startTime;
_logger.LogInformation(
"请求处理完成: {Method} {Path} - 状态码: {StatusCode} - 耗时: {Duration}ms",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
duration.TotalMilliseconds);
}
catch (Exception ex)
{
// 记录异常信息
var duration = DateTime.UtcNow - startTime;
_logger.LogError(ex,
"请求处理失败: {Method} {Path} - 耗时: {Duration}ms - 错误: {ErrorMessage}",
context.Request.Method,
context.Request.Path,
duration.TotalMilliseconds,
ex.Message);
// 重新抛出异常,让异常处理中间件处理
throw;
}
}
}
}
步骤2:创建扩展方法
using Microsoft.AspNetCore.Builder;
namespace CustomMiddleware
{
public static class RequestLoggingMiddlewareExtensions
{
public static IApplicationBuilder UseRequestLogging(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestLoggingMiddleware>();
}
}
}
步骤3:注册中间件
在Program.cs或Startup.cs中注册:
var builder = WebApplication.CreateBuilder(args);
// 添加日志服务
builder.Services.AddLogging();
var app = builder.Build();
// 配置中间件管道
app.UseRequestLogging(); // 自定义日志中间件
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
实战二:实现IMiddleware接口
对于需要依赖注入的复杂场景,可以实现IMiddleware接口:
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace CustomMiddleware
{
public class CustomHeaderMiddleware : IMiddleware
{
private readonly IConfiguration _configuration;
public CustomHeaderMiddleware(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// 在处理请求前添加自定义头部
context.Response.OnStarting(() =>
{
context.Response.Headers["X-Custom-Header"] =
_configuration["CustomHeaderValue"] ?? "DefaultValue";
context.Response.Headers["X-Request-ID"] =
Guid.NewGuid().ToString();
return Task.CompletedTask;
});
await next(context);
}
}
}
注册服务:
builder.Services.AddTransient<CustomHeaderMiddleware>();
使用中间件:
app.UseMiddleware<CustomHeaderMiddleware>();
实战三:条件性中间件处理
public class FeatureToggleMiddleware
{
private readonly RequestDelegate _next;
private readonly IFeatureManager _featureManager;
public FeatureToggleMiddleware(
RequestDelegate next,
IFeatureManager featureManager)
{
_next = next;
_featureManager = featureManager;
}
public async Task InvokeAsync(HttpContext context)
{
// 检查特性开关
if (await _featureManager.IsEnabledAsync("NewFeature"))
{
// 为新特性添加标记
context.Items["UsingNewFeature"] = true;
// 执行新特性逻辑
await HandleNewFeature(context);
}
else
{
// 执行旧逻辑
await _next(context);
}
}
private async Task HandleNewFeature(HttpContext context)
{
// 新特性的处理逻辑
context.Response.Headers["X-Feature-Version"] = "2.0";
await _next(context);
// 后处理逻辑
if (context.Response.StatusCode == 200)
{
await context.Response.WriteAsync("\n<!-- New Feature Enabled -->");
}
}
}
中间件最佳实践
1. 错误处理策略
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "未处理的异常");
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var code = HttpStatusCode.InternalServerError;
if (exception is NotFoundException) code = HttpStatusCode.NotFound;
else if (exception is UnauthorizedException) code = HttpStatusCode.Unauthorized;
var result = JsonSerializer.Serialize(new { error = exception.Message });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
2. 性能监控中间件
public class PerformanceMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<PerformanceMiddleware> _logger;
public PerformanceMiddleware(RequestDelegate next, ILogger<PerformanceMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
await _next(context);
stopwatch.Stop();
if (stopwatch.ElapsedMilliseconds > 1000)
{
_logger.LogWarning("慢请求: {Path} 耗时 {Elapsed}ms",
context.Request.Path, stopwatch.ElapsedMilliseconds);
}
}
}
中间件管道配置模式
1. 条件性中间件注册
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
}
// 生产环境特定的中间件
if (app.Environment.IsProduction())
{
app.UseHsts();
app.UseResponseCompression();
}
// 通用中间件
app.UseRequestLogging();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
2. 分支中间件管道
app.Map("/admin", adminApp =>
{
adminApp.UseMiddleware<AdminAuthenticationMiddleware>();
adminApp.UseMiddleware<AdminAuthorizationMiddleware>();
adminApp.MapControllers();
});
app.Map("/api", apiApp =>
{
apiApp.UseMiddleware<ApiVersionMiddleware>();
apiApp.UseMiddleware<RateLimitingMiddleware>();
apiApp.MapControllers();
});
测试自定义中间件
单元测试示例
[Test]
public async Task RequestLoggingMiddleware_LogsRequestInfo()
{
// 安排
var logger = new Mock<ILogger<RequestLoggingMiddleware>>();
var next = new Mock<RequestDelegate>();
var context = new DefaultHttpContext();
context.Request.Method = "GET";
context.Request.Path = "/test";
var middleware = new RequestLoggingMiddleware(next.Object, logger.Object);
// 执行
await middleware.InvokeAsync(context);
// 断言
logger.Verify(x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString().Contains("开始处理请求")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()),
Times.Once);
}
集成测试示例
[Fact]
public async Task CustomHeaderMiddleware_AddsCustomHeaders()
{
// 安排
var factory = new WebApplicationFactory<Program>();
var client = factory.CreateClient();
// 执行
var response = await client.GetAsync("/api/test");
// 断言
response.Headers.Should().ContainKey("X-Custom-Header");
response.Headers.Should().ContainKey("X-Request-ID");
}
常见问题与解决方案
问题1:中间件顺序错误
问题2:中间件性能优化
public class OptimizedMiddleware
{
private readonly RequestDelegate _next;
private static readonly ConcurrentDictionary<string, bool> _pathCache = new();
public OptimizedMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var path = context.Request.Path;
// 使用缓存避免重复计算
if (!_pathCache.TryGetValue(path, out var shouldProcess))
{
shouldProcess = ShouldProcessPath(path);
_pathCache[path] = shouldProcess;
}
if (shouldProcess)
{
// 处理逻辑
await ProcessRequest(context);
}
await _next(context);
}
private bool ShouldProcessPath(string path) =>
path.StartsWith("/api/", StringComparison.OrdinalIgnoreCase);
}
总结
通过本文的实战指南,你应该已经掌握了ASP.NET Core中间件开发的核心技能:
- 理解中间件管道工作原理:掌握了请求处理的生命周期和中间件的执行顺序
- 创建各类中间件:学会了基于类、IMiddleware接口、内联等多种中间件实现方式
- 实现高级功能:掌握了条件处理、错误处理、性能监控等高级中间件技术
- 测试和调试:学会了如何对中间件进行单元测试和集成测试
中间件是ASP.NET Core架构的核心,通过自定义中间件,你可以实现各种强大的功能,从简单的日志记录到复杂的业务逻辑处理。记住中间件开发的最佳实践,确保你的中间件是高效、可靠且易于维护的。
现在,是时候将这些知识应用到你的项目中,构建出更加强大和灵活的ASP.NET Core应用程序了!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



