第十七章:中间件与过滤器
一、ASP.NET Core 中间件概述
1.1 什么是中间件
中间件(Middleware)是组装到应用管道中的软件组件,用于处理 HTTP 请求和响应。每个中间件组件可以:
- 选择是否将请求传递给管道中的下一个组件
- 在管道中的下一个组件之前和之后执行操作
中间件按照注册的顺序依次执行,形成一个请求处理管道。
1.2 管道模型
请求 → 中间件1 → 中间件2 → 中间件3 → 端点(Controller/Action)
响应 ← 中间件1 ← 中间件2 ← 中间件3 ← 端点(Controller/Action)
每个中间件都有机会在请求到达端点之前和响应返回客户端之前执行自己的逻辑:
// 中间件管道的基本概念
app.Use(async (context, next) =>
{
// 请求到达时执行的逻辑(前置处理)
Console.WriteLine("中间件1 - 请求进入");
await next(); // 调用下一个中间件
// 响应返回时执行的逻辑(后置处理)
Console.WriteLine("中间件1 - 响应返回");
});
1.3 中间件与管道的执行顺序
| 阶段 | 执行方向 | 说明 |
|---|---|---|
| 请求阶段 | 从外到内 | 按注册顺序依次执行各中间件的前置逻辑 |
| 端点处理 | 中间 | 执行匹配到的 Controller/Action |
| 响应阶段 | 从内到外 | 按注册相反顺序依次执行各中间件的后置逻辑 |
二、内置中间件介绍
2.1 常用内置中间件
ASP.NET Core 提供了丰富的内置中间件,以下是推荐的注册顺序:
var builder = WebApplication.CreateBuilder(args);
// 注册服务
builder.Services.AddControllers();
builder.Services.AddAuthentication();
builder.Services.AddAuthorization();
builder.Services.AddCors();
var app = builder.Build();
// ① 异常处理中间件(最先注册,最后执行)
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
app.UseHsts();
}
// ② HTTPS 重定向
app.UseHttpsRedirection();
// ③ 静态文件
app.UseStaticFiles();
// ④ 路由
app.UseRouting();
// ⑤ 跨域
app.UseCors("AllowAll");
// ⑥ 认证
app.UseAuthentication();
// ⑦ 授权
app.UseAuthorization();
// ⑧ 端点映射
app.MapControllers();
app.Run();
2.2 各中间件功能说明
| 中间件 | 方法 | 功能 | 注意事项 |
|---|---|---|---|
| 异常处理 | UseExceptionHandler | 捕获全局异常 | 必须最先注册 |
| HTTPS重定向 | UseHttpsRedirection | HTTP 跳转 HTTPS | 在静态文件之前 |
| 静态文件 | UseStaticFiles | 提供 wwwroot 下的静态资源 | 在路由之前 |
| 路由 | UseRouting | URL 路由匹配 | 认证/授权之前 |
| CORS | UseCors | 跨域资源共享 | 在认证之前 |
| 认证 | UseAuthentication | 身份认证 | 在授权之前 |
| 授权 | UseAuthorization | 权限授权 | 在端点映射之前 |
| 端点映射 | MapControllers | 映射控制器路由 | 最后注册 |
三、自定义中间件开发
3.1 基于 RequestDelegate 的中间件
/// <summary>
/// 请求耗时统计中间件
/// </summary>
public class RequestTimingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestTimingMiddleware> _logger;
public RequestTimingMiddleware(RequestDelegate next,
ILogger<RequestTimingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
// 前置处理:记录请求信息
_logger.LogInformation(
"请求开始: {Method} {Path}",
context.Request.Method,
context.Request.Path);
await _next(context); // 调用下一个中间件
// 后置处理:记录耗时
stopwatch.Stop();
var elapsed = stopwatch.ElapsedMilliseconds;
_logger.LogInformation(
"请求结束: {Method} {Path} 耗时 {Elapsed}ms 状态码 {StatusCode}",
context.Request.Method,
context.Request.Path,
elapsed,
context.Response.StatusCode);
// 在响应头中添加耗时信息
context.Response.Headers["X-Response-Time"] = $"{elapsed}ms";
}
}
// 注册中间件的扩展方法
public static class RequestTimingMiddlewareExtensions
{
public static IApplicationBuilder UseRequestTiming(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestTimingMiddleware>();
}
}
3.2 基于 IMiddleware 接口的中间件
/// <summary>
/// 基于 IMiddleware 接口的 IP 白名单中间件
/// </summary>
public class IpWhitelistMiddleware : IMiddleware
{
private readonly ILogger<IpWhitelistMiddleware> _logger;
private readonly IConfiguration _configuration;
public IpWhitelistMiddleware(
ILogger<IpWhitelistMiddleware> logger,
IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var remoteIp = context.Connection.RemoteIpAddress?.ToString();
var allowedIps = _configuration
.GetSection("Security:AllowedIPs")
.Get<string[]>() ?? Array.Empty<string>();
if (allowedIps.Length > 0 && !allowedIps.Contains(remoteIp))
{
_logger.LogWarning("IP {IP} 不在白名单中,拒绝访问", remoteIp);
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsJsonAsync(new
{
Code = 403,
Message = "IP 地址不在白名单中"
});
return; // 不调用 next,短路管道
}
await next(context);
}
}
// 注册 IMiddleware 需要添加到 DI 容器
builder.Services.AddTransient<IpWhitelistMiddleware>();
// 使用中间件
app.UseMiddleware<IpWhitelistMiddleware>();
3.3 条件中间件
/// <summary>
/// 仅在特定条件下执行的中间件
/// </summary>
// 方式一:MapWhen - 分支管道
app.MapWhen(
context => context.Request.Path.StartsWithSegments("/api"),
apiApp =>
{
apiApp.UseMiddleware<ApiRateLimitMiddleware>();
});
// 方式二:UseWhen - 条件执行(不分支)
app.UseWhen(
context => context.Request.Path.StartsWithSegments("/admin"),
adminApp =>
{
adminApp.UseMiddleware<AdminAuditMiddleware>();
});
// 方式三:在中间件内部判断
public class ConditionalMiddleware
{
private readonly RequestDelegate _next;
public ConditionalMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 仅对 API 请求执行逻辑
if (context.Request.Path.StartsWithSegments("/api"))
{
context.Response.Headers["X-Custom-Header"] = "Furion";
}
await _next(context);
}
}
四、Furion 增强中间件
4.1 请求日志中间件
/// <summary>
/// Furion 请求日志中间件
/// </summary>
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)
{
// 读取请求体
context.Request.EnableBuffering();
var requestBody = await new StreamReader(context.Request.Body)
.ReadToEndAsync();
context.Request.Body.Position = 0;
// 记录请求信息
_logger.LogInformation("HTTP 请求信息:\n" +
" 方法: {Method}\n" +
" 路径: {Path}\n" +
" 查询: {Query}\n" +
" 请求体: {Body}\n" +
" IP: {IP}\n" +
" UserAgent: {UA}",
context.Request.Method,
context.Request.Path,
context.Request.QueryString,
requestBody,
context.Connection.RemoteIpAddress,
context.Request.Headers["User-Agent"]);
// 包装响应流以捕获响应体
var originalBodyStream = context.Response.Body;
using var responseBody = new MemoryStream();
context.Response.Body = responseBody;
await _next(context);
// 读取响应体
context.Response.Body.Seek(0, SeekOrigin.Begin);
var responseText = await new StreamReader(context.Response.Body)
.ReadToEndAsync();
context.Response.Body.Seek(0, SeekOrigin.Begin);
_logger.LogInformation("HTTP 响应信息:\n" +
" 状态码: {StatusCode}\n" +
" 响应体: {Body}",
context.Response.StatusCode,
responseText.Length > 1000
? responseText[..1000] + "..."
: responseText);
await responseBody.CopyToAsync(originalBodyStream);
}
}
4.2 性能监控中间件
/// <summary>
/// 性能监控中间件 - 慢请求告警
/// </summary>
public class PerformanceMonitorMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<PerformanceMonitorMiddleware> _logger;
private readonly int _slowRequestThresholdMs;
public PerformanceMonitorMiddleware(
RequestDelegate next,
ILogger<PerformanceMonitorMiddleware> logger,
IConfiguration configuration)
{
_next = next;
_logger = logger;
_slowRequestThresholdMs = configuration
.GetValue<int>("Performance:SlowRequestThresholdMs", 3000);
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
await _next(context);
stopwatch.Stop();
if (stopwatch.ElapsedMilliseconds > _slowRequestThresholdMs)
{
_logger.LogWarning(
"⚠️ 慢请求告警: {Method} {Path} 耗时 {Elapsed}ms(阈值 {Threshold}ms)",
context.Request.Method,
context.Request.Path,
stopwatch.ElapsedMilliseconds,
_slowRequestThresholdMs);
}
}
}
4.3 全局异常拦截中间件
/// <summary>
/// 全局异常处理中间件
/// </summary>
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalExceptionMiddleware> _logger;
private readonly IWebHostEnvironment _env;
public GlobalExceptionMiddleware(
RequestDelegate next,
ILogger<GlobalExceptionMiddleware> logger,
IWebHostEnvironment env)
{
_next = next;
_logger = logger;
_env = env;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "未处理的异常: {Message}", ex.Message);
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
var (statusCode, message) = exception switch
{
UnauthorizedAccessException => (401, "未经授权的访问"),
KeyNotFoundException => (404, "请求的资源不存在"),
ArgumentException e => (400, e.Message),
InvalidOperationException e => (400, e.Message),
_ => (500, "服务器内部错误")
};
context.Response.StatusCode = statusCode;
var response = new
{
Code = statusCode,
Success = false,
Message = message,
Detail = _env.IsDevelopment() ? exception.ToString() : null,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
await context.Response.WriteAsJsonAsync(response);
}
}
五、过滤器概述
5.1 过滤器类型
ASP.NET Core 提供了五种类型的过滤器,按执行顺序排列如下:
| 过滤器类型 | 接口 | 执行时机 | 典型用途 |
|---|---|---|---|
| Authorization | IAuthorizationFilter | 最先执行 | 权限验证 |
| Resource | IResourceFilter | 模型绑定前后 | 缓存、短路 |
| Action | IActionFilter | Action 执行前后 | 数据验证、审计日志 |
| Exception | IExceptionFilter | 异常发生时 | 异常处理 |
| Result | IResultFilter | 结果执行前后 | 结果包装、响应修改 |
5.2 过滤器管道
AuthorizationFilter → ResourceFilter(前) → 模型绑定 → ActionFilter(前)
→ Action 方法执行 → ActionFilter(后) → ResultFilter(前)
→ 结果执行 → ResultFilter(后) → ResourceFilter(后)
异常发生时:ExceptionFilter 介入处理
六、Action 过滤器
6.1 IActionFilter 同步实现
/// <summary>
/// 审计日志过滤器
/// </summary>
public class AuditLogActionFilter : IActionFilter
{
private readonly ILogger<AuditLogActionFilter> _logger;
private readonly IHttpContextAccessor _httpContextAccessor;
public AuditLogActionFilter(
ILogger<AuditLogActionFilter> logger,
IHttpContextAccessor httpContextAccessor)
{
_logger = logger;
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// Action 执行之前
/// </summary>
public void OnActionExecuting(ActionExecutingContext context)
{
var userId = _httpContextAccessor.HttpContext?.User?
.FindFirst("UserId")?.Value ?? "匿名";
var controller = context.RouteData.Values["controller"];
var action = context.RouteData.Values["action"];
var parameters = JsonSerializer.Serialize(context.ActionArguments);
_logger.LogInformation(
"审计日志 - 用户:{User} 访问:{Controller}/{Action} 参数:{Params}",
userId, controller, action, parameters);
}
/// <summary>
/// Action 执行之后
/// </summary>
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception != null)
{
_logger.LogError("审计日志 - 操作异常: {Error}",
context.Exception.Message);
}
else
{
_logger.LogInformation("审计日志 - 操作完成,状态码: {StatusCode}",
context.HttpContext.Response.StatusCode);
}
}
}
6.2 IAsyncActionFilter 异步实现
/// <summary>
/// 异步操作计时过滤器
/// </summary>
public class AsyncTimingActionFilter : IAsyncActionFilter
{
private readonly ILogger<AsyncTimingActionFilter> _logger;
public AsyncTimingActionFilter(ILogger<AsyncTimingActionFilter> logger)
{
_logger = logger;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
var stopwatch = Stopwatch.StartNew();
var actionName = context.ActionDescriptor.DisplayName;
_logger.LogInformation("Action 开始执行: {Action}", actionName);
// 执行 Action
var executedContext = await next();
stopwatch.Stop();
if (executedContext.Exception == null)
{
_logger.LogInformation(
"Action 执行完成: {Action},耗时: {Elapsed}ms",
actionName, stopwatch.ElapsedMilliseconds);
}
else
{
_logger.LogError(
"Action 执行异常: {Action},耗时: {Elapsed}ms,异常: {Error}",
actionName, stopwatch.ElapsedMilliseconds,
executedContext.Exception.Message);
}
}
}
七、异常过滤器
7.1 全局异常过滤器
/// <summary>
/// 全局异常过滤器
/// </summary>
public class GlobalExceptionFilter : IAsyncExceptionFilter
{
private readonly ILogger<GlobalExceptionFilter> _logger;
private readonly IWebHostEnvironment _env;
public GlobalExceptionFilter(
ILogger<GlobalExceptionFilter> logger,
IWebHostEnvironment env)
{
_logger = logger;
_env = env;
}
public async Task OnExceptionAsync(ExceptionContext context)
{
// 记录异常日志
_logger.LogError(context.Exception,
"未处理的异常: {Message}", context.Exception.Message);
// 构建错误响应
var errorResponse = new
{
Code = 500,
Success = false,
Message = _env.IsDevelopment()
? context.Exception.Message
: "服务器内部错误,请稍后重试",
TraceId = context.HttpContext.TraceIdentifier,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
context.Result = new JsonResult(errorResponse)
{
StatusCode = StatusCodes.Status500InternalServerError
};
// 标记异常已处理
context.ExceptionHandled = true;
}
}
7.2 特定异常处理过滤器
/// <summary>
/// 业务异常过滤器
/// </summary>
public class BusinessExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (context.Exception is BusinessException bizEx)
{
context.Result = new JsonResult(new
{
Code = bizEx.ErrorCode,
Success = false,
Message = bizEx.Message,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
})
{
StatusCode = StatusCodes.Status400BadRequest
};
context.ExceptionHandled = true;
}
}
}
/// <summary>
/// 自定义业务异常
/// </summary>
public class BusinessException : Exception
{
public int ErrorCode { get; }
public BusinessException(int errorCode, string message)
: base(message)
{
ErrorCode = errorCode;
}
}
八、结果过滤器与授权过滤器
8.1 结果过滤器
/// <summary>
/// 响应缓存过滤器
/// </summary>
public class ResponseCacheFilter : IAsyncResultFilter
{
private readonly IDistributedCache _cache;
public ResponseCacheFilter(IDistributedCache cache)
{
_cache = cache;
}
public async Task OnResultExecutionAsync(
ResultExecutingContext context,
ResultExecutionDelegate next)
{
// 仅缓存 GET 请求
if (context.HttpContext.Request.Method == "GET")
{
var cacheKey = $"response:{context.HttpContext.Request.Path}";
var cachedResult = await _cache.GetStringAsync(cacheKey);
if (!string.IsNullOrEmpty(cachedResult))
{
context.Result = new ContentResult
{
Content = cachedResult,
ContentType = "application/json",
StatusCode = 200
};
return; // 短路,不执行后续管道
}
}
await next();
}
}
8.2 授权过滤器
/// <summary>
/// 自定义权限验证过滤器
/// </summary>
public class PermissionAuthorizationFilter : IAsyncAuthorizationFilter
{
private readonly IPermissionService _permissionService;
public PermissionAuthorizationFilter(IPermissionService permissionService)
{
_permissionService = permissionService;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
if (!user.Identity?.IsAuthenticated ?? true)
{
context.Result = new JsonResult(new
{
Code = 401,
Message = "请先登录"
})
{
StatusCode = StatusCodes.Status401Unauthorized
};
return;
}
var userId = user.FindFirst("UserId")?.Value;
var path = context.HttpContext.Request.Path.Value;
var method = context.HttpContext.Request.Method;
var hasPermission = await _permissionService
.CheckPermissionAsync(userId, path, method);
if (!hasPermission)
{
context.Result = new JsonResult(new
{
Code = 403,
Message = "权限不足,无法访问此资源"
})
{
StatusCode = StatusCodes.Status403Forbidden
};
}
}
}
九、全局过滤器注册与执行顺序
9.1 全局过滤器注册
// Program.cs 中注册全局过滤器
builder.Services.AddControllers(options =>
{
// 注册全局过滤器(按添加顺序执行)
options.Filters.Add<GlobalExceptionFilter>(); // 异常过滤器
options.Filters.Add<AuditLogActionFilter>(); // 审计日志过滤器
options.Filters.Add<AsyncTimingActionFilter>(); // 计时过滤器
// 带 DI 的过滤器注册
options.Filters.AddService<PermissionAuthorizationFilter>();
// 指定执行顺序
options.Filters.Add(new AsyncTimingActionFilter(null) { Order = 1 });
options.Filters.Add(new AuditLogActionFilter(null, null) { Order = 2 });
});
9.2 控制器或方法级别过滤器
/// <summary>
/// 使用特性方式注册过滤器
/// </summary>
[ServiceFilter(typeof(AuditLogActionFilter))] // 控制器级别
public class OrderAppService : IDynamicApiController
{
[TypeFilter(typeof(AsyncTimingActionFilter))] // 方法级别
public async Task<OrderDto> GetOrder(int id)
{
// ...
}
// 使用自定义特性过滤器
[AuditLog("删除订单")]
public async Task DeleteOrder(int id)
{
// ...
}
}
/// <summary>
/// 自定义审计日志特性
/// </summary>
public class AuditLogAttribute : ActionFilterAttribute
{
private readonly string _operation;
public AuditLogAttribute(string operation)
{
_operation = operation;
}
public override async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<AuditLogAttribute>>();
var userId = context.HttpContext.User
.FindFirst("UserId")?.Value ?? "匿名";
logger.LogInformation("审计: 用户 {User} 执行 {Op}", userId, _operation);
await next();
}
}
9.3 过滤器执行顺序详解
| 优先级 | 说明 | 示例 |
|---|---|---|
| Order 属性 | 数值越小越先执行 | Order = -1 最先执行 |
| 作用域 | 全局 → 控制器 → 方法 | 全局过滤器先于控制器级别 |
| 类型 | Authorization → Resource → Action → Exception → Result | 按类型分层执行 |
十、中间件 vs 过滤器的选择
10.1 对比分析
| 特性 | 中间件 | 过滤器 |
|---|---|---|
| 作用范围 | 所有 HTTP 请求 | 仅 MVC/API 请求 |
| 执行时机 | 管道最外层 | MVC 管道内部 |
| 能否访问 MVC 上下文 | 否 | 是(ActionContext等) |
| 适用场景 | 跨切面关注点 | 与 Action 相关的逻辑 |
| 注册方式 | app.UseXxx() | options.Filters.Add() |
| 短路能力 | 不调用 next() | 设置 context.Result |
| DI 支持 | 构造函数注入 | ServiceFilter/TypeFilter |
10.2 选择指南
// ✅ 使用中间件的场景:
// 1. 全局日志记录
// 2. 全局异常处理
// 3. 请求/响应修改
// 4. 性能监控
// 5. 认证/授权(通用)
// 6. CORS
// 7. 静态文件服务
// 8. 健康检查
// ✅ 使用过滤器的场景:
// 1. 数据验证
// 2. 审计日志(需要 Action 信息)
// 3. 结果格式化
// 4. 特定接口的权限控制
// 5. 事务管理
// 6. 接口缓存
十一、AOP 面向切面编程
11.1 Furion 中的 AOP 实现
/// <summary>
/// 事务管理 AOP 过滤器
/// </summary>
public class TransactionActionFilter : IAsyncActionFilter
{
private readonly IServiceProvider _serviceProvider;
public TransactionActionFilter(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
// 检查是否标记了事务特性
var hasTransaction = context.ActionDescriptor
.EndpointMetadata
.Any(m => m is TransactionalAttribute);
if (!hasTransaction)
{
await next();
return;
}
// 开启事务
var dbContext = _serviceProvider
.GetRequiredService<DefaultDbContext>();
using var transaction = await dbContext.Database
.BeginTransactionAsync();
try
{
var result = await next();
if (result.Exception == null)
{
await transaction.CommitAsync();
}
else
{
await transaction.RollbackAsync();
}
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
/// <summary>
/// 事务标记特性
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class TransactionalAttribute : Attribute { }
// 使用示例
public class OrderAppService : IDynamicApiController
{
[Transactional]
public async Task<OrderDto> CreateOrder(CreateOrderInput input)
{
// 此方法内的所有数据库操作将在同一事务中执行
var order = await _orderRepository.InsertAsync(new Order { ... });
await _inventoryService.DeductStockAsync(input.ProductId, input.Quantity);
await _paymentService.CreatePaymentAsync(order.Entity.Id, input.Amount);
return order.Entity.Adapt<OrderDto>();
}
}
11.2 方法拦截器模式
/// <summary>
/// 通用方法拦截器
/// </summary>
public class MethodInterceptor : IAsyncActionFilter
{
private readonly ILogger<MethodInterceptor> _logger;
public MethodInterceptor(ILogger<MethodInterceptor> logger)
{
_logger = logger;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
var methodName = context.ActionDescriptor.DisplayName;
var parameters = context.ActionArguments;
// 前置拦截
_logger.LogDebug("拦截器 - 方法调用前: {Method}, 参数: {Params}",
methodName, JsonSerializer.Serialize(parameters));
var result = await next();
// 后置拦截
if (result.Result is ObjectResult objectResult)
{
_logger.LogDebug("拦截器 - 方法调用后: {Method}, 返回值: {Result}",
methodName, JsonSerializer.Serialize(objectResult.Value));
}
}
}
十二、最佳实践与性能考虑
12.1 中间件最佳实践
// ✅ 正确:轻量级中间件
public class LightweightMiddleware
{
private readonly RequestDelegate _next;
public LightweightMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 仅在必要时执行逻辑
if (context.Request.Path.StartsWithSegments("/api"))
{
context.Response.Headers["X-Api-Version"] = "1.0";
}
await _next(context);
}
}
// ❌ 错误:在中间件中做大量同步操作
public class HeavyMiddleware
{
private readonly RequestDelegate _next;
public HeavyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 避免同步阻塞操作
// Thread.Sleep(1000); ❌
// var data = File.ReadAllText("large-file.txt"); ❌
// 使用异步操作
await Task.Delay(1000); // ✅(仅为示例)
var data = await File.ReadAllTextAsync("large-file.txt"); // ✅
await _next(context);
}
}
12.2 性能优化建议
| 建议 | 说明 |
|---|---|
| 避免不必要的中间件 | 每个中间件都有性能开销 |
| 合理使用短路 | 尽早返回可以减少不必要的处理 |
| 异步优先 | 使用 IAsyncActionFilter 而非 IActionFilter |
| 避免重复读取请求体 | 使用 EnableBuffering() 并注意内存消耗 |
| 缓存常用数据 | 避免每次请求都查询数据库 |
| 控制日志级别 | 生产环境避免记录详细请求/响应体 |
12.3 完整注册示例
var builder = WebApplication.CreateBuilder(args);
// 注册过滤器相关服务
builder.Services.AddTransient<AuditLogActionFilter>();
builder.Services.AddTransient<PermissionAuthorizationFilter>();
builder.Services.AddTransient<IpWhitelistMiddleware>();
builder.Services.AddControllers(options =>
{
// 全局过滤器
options.Filters.Add<GlobalExceptionFilter>();
options.Filters.AddService<AuditLogActionFilter>();
});
var app = builder.Build();
// 中间件注册顺序
app.UseMiddleware<GlobalExceptionMiddleware>();
app.UseRequestTiming();
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
通过本章的学习,你已经全面掌握了 ASP.NET Core 中间件和过滤器的使用方法,以及 Furion 框架中的增强实践。合理使用中间件和过滤器可以实现代码的高度复用和关注点分离。在下一章中,我们将学习模板引擎与视图的使用。