中间件管道构建原理
大约 10 分钟约 3090 字
中间件管道构建原理
简介
ASP.NET Core 的请求处理管道由一系列中间件组成,每个中间件可以处理请求、调用下一个中间件或短路请求。理解管道的构建原理、RequestDelegate 和 ApplicationBuilder 的内部机制,有助于编写高效的自定义中间件。
特点
管道构建原理
RequestDelegate 链
// ASP.NET Core 中间件本质是:
// Func<RequestDelegate, RequestDelegate>
// 即:接收下一个中间件,返回一个新的请求委托
// 手动构建管道(不使用 ApplicationBuilder)
RequestDelegate pipeline = async context =>
{
await context.Response.WriteAsync("终端中间件");
};
// 这是最简单的管道(只有一个终端中间件)
// 添加中间件(从后向前构建)
// 中间件 3(终端)
RequestDelegate app = async context =>
{
await context.Response.WriteAsync("终端");
};
// 中间件 2
app = WrapMiddleware(app, async (next, context) =>
{
await context.Response.WriteAsync("中间件2 前\n");
await next(context);
await context.Response.WriteAsync("中间件2 后\n");
});
// 中间件 1
app = WrapMiddleware(app, async (next, context) =>
{
await context.Response.WriteAsync("中间件1 前\n");
await next(context);
await context.Response.WriteAsync("中间件1 后\n");
});
// 执行 pipeline → 输出:
// 中间件1 前
// 中间件2 前
// 终端
// 中间件2 后
// 中间件1 后
static RequestDelegate WrapMiddleware(RequestDelegate next,
Func<RequestDelegate, HttpContext, Task> middleware)
{
return context => middleware(next, context);
}ApplicationBuilder 内部
// ApplicationBuilder 的核心实现
public class ApplicationBuilder : IApplicationBuilder
{
private readonly List<Func<RequestDelegate, RequestDelegate>> _components = new();
// Use 添加中间件
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
_components.Add(middleware);
return this;
}
// Build 组装管道(从后向前折叠)
public RequestDelegate Build()
{
RequestDelegate app = context =>
{
// 默认终端中间件(404)
context.Response.StatusCode = 404;
return Task.CompletedTask;
};
// 反向遍历,逐层包装
foreach (var component in _components.Reverse<Func<RequestDelegate, RequestDelegate>>())
{
app = component(app);
}
return app;
}
}
// 理解管道组装过程:
// 假设有3个中间件:M1, M2, M3
// 注册顺序:Use(M1), Use(M2), Use(M3)
// Build 后:M1(M2(M3(terminal)))
// 执行顺序:M1 → M2 → M3 → terminal → M3 → M2 → M1
// app.Use() 的内部
public static IApplicationBuilder Use(this IApplicationBuilder app,
Func<HttpContext, Func<Task>, Task> middleware)
{
return app.Use(next =>
{
return context =>
{
Func<Task> nextFunc = () => next(context);
return middleware(context, nextFunc);
};
});
}自定义中间件
约束式中间件
// 方式 1:内联中间件
app.Use(async (context, next) =>
{
// 请求处理前
var sw = Stopwatch.StartNew();
await next(); // 调用下一个中间件
// 响应处理后
sw.Stop();
context.Response.Headers.Append("X-Elapsed-Ms", sw.ElapsedMilliseconds.ToString());
});
// 方式 2:约定式中间件类
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 sw = Stopwatch.StartNew();
try
{
await _next(context);
}
finally
{
sw.Stop();
_logger.LogInformation("{Method} {Path} → {StatusCode} ({Ms}ms)",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
sw.ElapsedMilliseconds);
}
}
}
// 注册
app.UseMiddleware<RequestTimingMiddleware>();
// 扩展方法
public static class RequestTimingExtensions
{
public static IApplicationBuilder UseRequestTiming(this IApplicationBuilder app)
=> app.UseMiddleware<RequestTimingMiddleware>();
}IMiddleware 接口(DI 友好)
// 方式 3:IMiddleware 接口(支持 Scoped 依赖注入)
public class TenantMiddleware : IMiddleware
{
private readonly ITenantService _tenantService;
public TenantMiddleware(ITenantService tenantService)
{
_tenantService = tenantService;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var tenantId = context.Request.Headers["X-Tenant-Id"].FirstOrDefault();
if (tenantId != null)
{
_tenantService.SetTenant(tenantId); // Scoped 服务
}
await next(context);
}
}
// 注册(注意:必须注册为 Scoped 或 Transient)
builder.Services.AddScoped<TenantMiddleware>();
app.UseMiddleware<TenantMiddleware>();
// IMiddleware vs 约定式:
// 约定式:构造函数在管道构建时调用(Singleton 生命周期)
// IMiddleware:每次请求创建新实例(支持 Scoped 注入)
// 性能:约定式更快(无每次请求的创建开销)Map 和 UseWhen
分支管道
// Map — 路径匹配分支(短路)
app.Map("/api", apiApp =>
{
apiApp.UseMiddleware<ApiAuthMiddleware>();
apiApp.MapGet("/users", () => "Users API");
apiApp.MapGet("/orders", () => "Orders API");
});
app.Map("/admin", adminApp =>
{
adminApp.UseMiddleware<AdminAuthMiddleware>();
adminApp.MapGet("/", () => "Admin Panel");
});
// MapWhen — 条件分支
app.MapWhen(context => context.Request.Headers.ContainsKey("X-Internal"),
internalApp =>
{
internalApp.UseMiddleware<InternalApiMiddleware>();
});
// UseWhen — 条件中间件(不短路)
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"),
apiApp =>
{
// 只对 /api 路径生效
apiApp.UseMiddleware<ApiLoggingMiddleware>();
// 不会短路,继续主管道
});短路中间件
条件性响应
异常处理中间件
全局异常处理管道
/// <summary>
/// 异常处理中间件 — 必须放在管道最外层
/// </summary>
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalExceptionMiddleware> _logger;
private readonly IHostEnvironment _env;
public GlobalExceptionMiddleware(
RequestDelegate next,
ILogger<GlobalExceptionMiddleware> logger,
IHostEnvironment env)
{
_next = next;
_logger = logger;
_env = env;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
// 已经开始写响应的异常不能再写
if (context.Response.HasStarted)
{
_logger.LogError(ex, "响应已开始,无法处理异常");
throw;
}
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var traceId = context.TraceIdentifier;
var path = context.Request.Path.Value;
_logger.LogError(ex, "未处理异常 [{TraceId}] {Path}", traceId, path);
context.Response.ContentType = "application/json; charset=utf-8";
var (statusCode, code, message) = ex switch
{
BizException biz => (biz.StatusCode, biz.Code, biz.Message),
NotFoundException => (404, "NOT_FOUND", ex.Message),
UnauthorizedAccessException => (401, "UNAUTHORIZED", "未授权"),
ValidationException => (400, "VALIDATION_ERROR", "参数验证失败"),
OperationCanceledException => (499, "CANCELLED", "请求已取消"),
TimeoutException => (504, "TIMEOUT", "请求超时"),
_ => (500, "INTERNAL_ERROR",
_env.IsDevelopment() ? ex.Message : "服务器内部错误")
};
context.Response.StatusCode = statusCode;
var errorResponse = new
{
code,
message,
traceId,
timestamp = DateTime.UtcNow
};
// 开发环境返回堆栈
if (_env.IsDevelopment())
{
var devResponse = new
{
errorResponse.code,
errorResponse.message,
errorResponse.traceId,
errorResponse.timestamp,
detail = ex.ToString()
};
await context.Response.WriteAsJsonAsync(devResponse);
}
else
{
await context.Response.WriteAsJsonAsync(errorResponse);
}
}
}
// 注册顺序很重要
app.UseMiddleware<GlobalExceptionMiddleware>(); // 第一层
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();认证中间件原理
认证流程深入
/// <summary>
/// 自定义认证中间件(理解 UseAuthentication 的内部原理)
/// </summary>
public class CustomAuthenticationMiddleware
{
private readonly RequestDelegate _next;
public CustomAuthenticationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 1. 获取默认认证方案
var authSchemeProvider = context.RequestServices
.GetRequiredService<IAuthenticationSchemeProvider>();
var defaultScheme = await authSchemeProvider.GetDefaultAuthenticateSchemeAsync();
if (defaultScheme != null)
{
// 2. 获取对应的 Handler
var handlerProvider = context.RequestServices
.GetRequiredService<IAuthenticationHandlerProvider>();
var handler = await handlerProvider.GetHandlerAsync(context, defaultScheme.Name);
if (handler != null)
{
// 3. 执行认证
var result = await handler.AuthenticateAsync();
if (result?.Principal != null)
{
// 4. 设置 User
context.User = result.Principal;
}
if (result != null && result.Failure != null)
{
_logger.LogWarning("认证失败: {Error}", result.Failure.Message);
}
}
}
// 5. 继续管道
await _next(context);
}
}
// UseAuthentication 内部做了什么:
// 1. 从 IAuthenticationSchemeProvider 获取默认方案
// 2. 从 IAuthenticationHandlerProvider 获取对应 Handler
// 3. 调用 Handler.AuthenticateAsync()
// 4. 将结果(ClaimsPrincipal)设置到 HttpContext.User
// 5. 将 AuthenticateResult 存入 HttpContext.Features响应缓存中间件
输出缓存
// .NET 7+ 输出缓存(替代 ResponseCache)
builder.Services.AddOutputCache(options =>
{
// 默认策略
options.DefaultPolicy = new OutputCachePolicy
{
Duration = TimeSpan.FromSeconds(60),
};
// 命名策略
options.AddPolicy("ProductsCache", new OutputCachePolicy
{
Duration = TimeSpan.FromMinutes(5),
VaryByQueryKeys = new[] { "category", "page" },
VaryByHeader = "Accept-Language"
});
options.AddPolicy("NoCache", new OutputCachePolicy { NoCache = true });
});
app.UseOutputCache();
// 使用
app.MapGet("/api/products", () => GetProducts())
.CacheOutput("ProductsCache");
// 按用户缓存(需要 VaryByValue)
app.MapGet("/api/profile", () => GetUserProfile())
.CacheOutput(policy =>
{
policy.SetVaryByValue(context =>
context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "anonymous");
policy.Duration = TimeSpan.FromMinutes(10);
});
// 缓存标记 — 用于主动失效
app.MapGet("/api/config", () => GetConfig())
.CacheOutput(policy =>
{
policy.Duration = TimeSpan.FromHours(1);
policy.Tags = new[] { "config" };
});
// 主动清除缓存
app.MapPost("/api/admin/refresh-config", async (IOutputCacheStore cache, CancellationToken ct) =>
{
await cache.EvictByTagAsync("config", ct);
return Results.Ok("缓存已清除");
});中间件性能最佳实践
性能优化建议
/// <summary>
/// 中间件性能注意事项
/// </summary
// 1. 使用内联中间件减少分配
app.Use(async (context, next) =>
{
// 简单逻辑用内联,避免类实例化开销
context.Response.Headers.Append("X-Request-Id", Guid.NewGuid().ToString());
await next();
});
// 2. 避免在中间件中做阻塞操作
// ❌ 错误:同步阻塞
app.Use(async (context, next) =>
{
Thread.Sleep(1000); // 阻塞线程
await next();
});
// ✅ 正确:异步等待
app.Use(async (context, next) =>
{
await Task.Delay(1000); // 不阻塞线程
await next();
});
// 3. 避免不必要的中间件注册
// ❌ 每个请求都执行条件判断
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/api"))
{
// 只有 /api 需要,但其他路径也要经过这个判断
}
await next();
});
// ✅ 使用 MapWhen 或 UseWhen 分支
app.MapWhen(context => context.Request.Path.StartsWithSegments("/api"),
apiApp => { /* API 专用中间件 */ });
// 4. 减少 HttpContext.Items 的使用
// ❌ 频繁装箱拆箱
context.Items["key"] = someObject; // object 类型
// ✅ 使用强类型 Feature
public class RequestContextFeature
{
public string CorrelationId { get; set; } = "";
public DateTime StartTime { get; set; }
}
context.Features.Set(new RequestContextFeature { CorrelationId = Guid.NewGuid().ToString() });
// 5. 中间件执行时间统计
public class PerformanceLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public PerformanceLoggingMiddleware(RequestDelegate next, ILogger logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var sw = ValueStopwatch.StartNew();
try
{
await _next(context);
}
finally
{
var elapsed = sw.GetElapsedTime();
if (elapsed.TotalMilliseconds > 500)
{
_logger.LogWarning("慢请求: {Method} {Path} ({Elapsed}ms)",
context.Request.Method, context.Request.Path,
elapsed.TotalMilliseconds);
}
}
}
}
// 使用结构体 Stopwatch 避免堆分配
internal struct ValueStopwatch
{
private long _startTimestamp;
public static ValueStopwatch StartNew()
{
var sw = new ValueStopwatch();
sw._startTimestamp = Stopwatch.GetTimestamp();
return sw;
}
public TimeSpan GetElapsedTime() =>
TimeSpan.FromTicks(Stopwatch.GetTimestamp() - _startTimestamp);
}// 短路:不调用 next,直接返回响应
public class RateLimitingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private int _requestCount;
private DateTime _windowStart = DateTime.UtcNow;
private readonly int _maxRequests = 100;
private readonly TimeSpan _window = TimeSpan.FromMinutes(1);
public RateLimitingMiddleware(RequestDelegate next, ILogger<RateLimitingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// 滑动窗口限流
if (DateTime.UtcNow - _windowStart > _window)
{
_requestCount = 0;
_windowStart = DateTime.UtcNow;
}
if (++_requestCount > _maxRequests)
{
// 短路:直接返回 429
context.Response.StatusCode = 429;
context.Response.Headers["Retry-After"] = _window.TotalSeconds.ToString();
await context.Response.WriteAsync("Too Many Requests");
return; // 不调用 _next
}
await _next(context);
}
}
// 健康检查短路
app.Map("/health", appBuilder =>
{
appBuilder.Run(async context =>
{
// Run 是终端中间件(没有 next)
await context.Response.WriteAsync("Healthy");
});
});优点
缺点
总结
ASP.NET Core 中间件管道本质是 Func<RequestDelegate, RequestDelegate> 的链式调用。ApplicationBuilder 在 Build() 时从后向前折叠中间件,形成洋葱模型。请求从外到内、响应从内到外流转。自定义中间件三种方式:内联 app.Use()、约定式类、IMiddleware 接口。Map 创建路径分支(短路),UseWhen 创建条件分支(不短路)。短路中间件不调用 next,直接返回响应。中间件注册顺序至关重要——认证必须在授权之前、异常处理应在最外层。
关键知识点
- 先分清这个主题位于请求链路、后台任务链路还是基础设施链路。
- 服务端主题通常不只关心功能正确,还关心稳定性、性能和可观测性。
- 任何框架能力都要结合配置、生命周期、异常传播和外部依赖一起看。
- 框架能力的真正重点是它在请求链路中的位置和对上下游的影响。
项目落地视角
- 画清请求进入、业务执行、外部调用、日志记录和错误返回的完整路径。
- 为关键链路补齐超时、重试、熔断、追踪和结构化日志。
- 把配置与敏感信息分离,并明确不同环境的差异来源。
- 画清执行顺序、入参来源、失败返回和日志记录点。
常见误区
- 只会堆中间件或组件,不知道它们在链路中的执行顺序。
- 忽略生命周期和线程池、连接池等运行时资源约束。
- 没有监控和测试就对性能或可靠性下结论。
- 知道 API 名称,却不知道它应该放在请求链路的哪个位置。
进阶路线
- 继续向运行时行为、可观测性、发布治理和微服务协同深入。
- 把主题和数据库、缓存、消息队列、认证授权联动起来理解。
- 沉淀团队级模板,包括统一异常处理、配置约定和基础设施封装。
- 继续补齐协议选型、网关治理、端点可观测性和契约演进策略。
适用场景
- 当你准备把《中间件管道构建原理》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合 API 服务、后台任务、实时通信、认证授权和微服务协作场景。
- 当需求开始涉及稳定性、性能、可观测性和发布流程时,这类主题会成为基础设施能力。
落地建议
- 先定义请求链路与失败路径,再决定中间件、过滤器、服务边界和依赖方式。
- 为关键链路补日志、指标、追踪、超时与重试策略。
- 环境配置与敏感信息分离,避免把生产参数写死在代码或镜像里。
排错清单
- 先确认问题发生在路由、模型绑定、中间件、业务层还是基础设施层。
- 检查 DI 生命周期、配置来源、序列化规则和认证上下文。
- 查看线程池、连接池、缓存命中率和外部依赖超时。
复盘问题
- 如果把《中间件管道构建原理》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《中间件管道构建原理》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《中间件管道构建原理》最大的收益和代价分别是什么?
