






















ASP.NET Core 中间件 / Filter / Minimal API
题:实现一个带 CorrelationId、请求日志、异常统一处理的中间件链。
要求:支持 Scoped 服务注入 + 性能无损。
先定义统一的异常响应模型、CorrelationId 存取器(支持 Scoped 注入) // CorrelationId 存取器(Scoped 服务,每个请求独立实例) public interface ICorrelationIdAccessor { string GetCorrelationId(); void SetCorrelationId(string correlationId); } public class CorrelationIdAccessor : ICorrelationIdAccessor { private string _correlationId; public string GetCorrelationId() => _correlationId; public void SetCorrelationId(string correlationId) => _correlationId = correlationId; } // 统一异常响应模型 public class ApiErrorResponse { public string TraceId { get; set; } public int StatusCode { get; set; } public string Message { get; set; } public DateTime Timestamp { get; set; } = DateTime.UtcNow; } 从请求头读取 / 生成 CorrelationId,存入 Scoped 服务 + 响应头 using Microsoft.AspNetCore.Http; // 纯静态类 + 委托实现,无实例化开销,性能最优 public static class CorrelationIdMiddleware { // 约定请求头/响应头名称 private const string CorrelationIdHeader = "X-Correlation-ID"; public static RequestDelegate UseCorrelationId(this RequestDelegate next) { // 异步委托,非阻塞,无闭包捕获 return async context => { // 从 Scoped 服务获取存取器(官方推荐,支持作用域) var correlationIdAccessor = context.RequestServices.GetRequiredService<ICorrelationIdAccessor>(); // 读取请求头的 CorrelationId,不存在则生成 GUID var correlationId = context.Request.Headers.TryGetValue(CorrelationIdHeader, out var id) ? id.ToString() : Guid.NewGuid().ToString("N"); // 存入 Scoped 服务 + 响应头 correlationIdAccessor.SetCorrelationId(correlationId); context.Response.Headers.TryAdd(CorrelationIdHeader, correlationId); // 传递到下一个中间件 await next(context); }; } } 捕获所有未处理异常,返回标准化 JSON 响应,记录错误日志 using Microsoft.AspNetCore.Http; using System.Net; using System.Text.Json; public static class ExceptionHandlingMiddleware { // 序列化配置(静态只读,全局复用,性能极高) private static readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false }; public static RequestDelegate UseExceptionHandling(this RequestDelegate next) { return async context => { try { await next(context); } catch (Exception ex) { // 获取 Scoped CorrelationId var correlationId = context.RequestServices .GetRequiredService<ICorrelationIdAccessor>() .GetCorrelationId(); // 获取日志服务(支持 Scoped/Transient/Singleton) var logger = context.RequestServices .GetRequiredService<ILogger<Program>>(); // 记录错误日志(携带 CorrelationId) logger.LogError(ex, "请求发生异常 | TraceId: {TraceId}", correlationId); // 统一响应处理 context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; var errorResponse = new ApiErrorResponse { TraceId = correlationId, StatusCode = context.Response.StatusCode, Message = ex.Message // 生产环境可替换为友好提示 }; // 序列化写入响应 await JsonSerializer.SerializeAsync(context.Response.Body, errorResponse, _jsonOptions); } }; } } 记录请求开始 / 结束、耗时、路径、状态码、CorrelationId,零内存分配 using Microsoft.AspNetCore.Http; using System.Diagnostics; public static class RequestLoggingMiddleware { public static RequestDelegate UseRequestLogging(this RequestDelegate next) { return async context => { var logger = context.RequestServices.GetRequiredService<ILogger<Program>>(); var correlationIdAccessor = context.RequestServices.GetRequiredService<ICorrelationIdAccessor>(); var method = context.Request.Method; var path = context.Request.Path; var traceId = correlationIdAccessor.GetCorrelationId(); // 高精度计时(性能远优于 DateTime) var stopwatch = Stopwatch.StartNew(); try { logger.LogInformation( "请求开始 | Method: {Method} | Path: {Path} | TraceId: {TraceId}", method, path, traceId); await next(context); } finally { stopwatch.Stop(); var statusCode = context.Response.StatusCode; var elapsedMs = stopwatch.ElapsedMilliseconds; logger.LogInformation( "请求结束 | Method: {Method} | Path: {Path} | Status: {StatusCode} | 耗时: {ElapsedMs}ms | TraceId: {TraceId}", method, path, statusCode, elapsedMs, traceId); } }; } } Program.cs 注册 + 配置(Minimal API 完整版) 关键要点: 1. 注册顺序:异常处理 → CorrelationId → 请求日志 → 路由(必须严格按这个顺序) 2. Scoped 服务注册:ICorrelationIdAccessor 作用域注入 3. 纯委托中间件,无反射、无实例化、无性能损耗 var builder = WebApplication.CreateBuilder(args); // 1. 注册 Scoped 服务(核心:支持作用域注入,每个请求独立实例) builder.Services.AddScoped<ICorrelationIdAccessor, CorrelationIdAccessor>(); // 2. 添加日志(框架默认集成,无需额外配置) builder.Logging.AddConsole(); var app = builder.Build(); // 3. 注册高性能中间件链(顺序绝对不能错!) app.Use(async (context, next) => { // 异常处理(最外层,捕获所有异常) await ExceptionHandlingMiddleware.UseExceptionHandling(next)(context); }); app.Use(async (context, next) => { // CorrelationId(第二层,生成唯一标识) await CorrelationIdMiddleware.UseCorrelationId(next)(context); }); app.Use(async (context, next) => { // 请求日志(第三层,记录完整请求生命周期) await RequestLoggingMiddleware.UseRequestLogging(next)(context); }); // 4. Minimal API 路由测试 app.MapGet("/", () => "Hello World!"); // 测试异常接口 app.MapGet("/error", () => { throw new InvalidOperationException("测试业务异常"); }); // 测试在业务中获取 CorrelationId(验证 Scoped 服务生效) app.MapGet("/trace", (ICorrelationIdAccessor correlationIdAccessor) => { return new { Message = "当前请求 TraceId", TraceId = correlationIdAccessor.GetCorrelationId() }; }); app.Run();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。