中间件是ASP.NET Core中最受欢迎的功能之一。它让你能够集中控制请求/响应处理,而且极其灵活。只需几行app.Use(...)代码,你就能实现日志记录、身份验证或CORS功能。
但问题在于:中间件并非总是解决问题的最佳工具。
试图在中间件中塞入过多逻辑会导致代码混乱、路由出错以及应用程序脆弱不堪。要构建清晰、可维护的API,你需要知道中间件在哪些方面表现出色,而在哪些方面则不尽如人意。
让我们来详细分析一下。
本质上,中间件是一个函数,它可以在HTTP请求/响应流经ASP.NET Core管道时对其进行检查、修改或短路处理。
每个中间件组件:
这是一个经典示例:
app.Use(async (context, next) =>
{
Console.WriteLine("Before");
await next.Invoke();
Console.WriteLine("After");
});
执行流程会自上而下地穿过管道,然后再回溯——可以把它想象成嵌套的try...finally语句。
当你希望不考虑端点逻辑而对请求进行广泛控制时,就可以使用中间件。
(图片将被放大显示)
什么是中间件
中间件在处理横切关注点时表现出色——这些逻辑应该在每个请求上运行,并且不依赖于路由或模型绑定。
最适合的使用场景:
public class LoggingMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
Console.WriteLine($"Request: {context.Request.Method} {context.Request.Path}");
await next(context);
Console.WriteLine($"Response: {context.Response.StatusCode}");
}
}
然后在Startup.cs中注册它:
builder.Services.AddTransient<LoggingMiddleware>();
app.UseMiddleware<LoggingMiddleware>();
简单、全局、清晰。这正是中间件的最佳应用方式。
(图片将被放大显示)
尽管中间件功能强大,但它并非万能药。它无法访问许多你经常需要的东西——比如路由参数、模型状态或复杂的依赖注入图。
不要将中间件用于:
app.Use(async (context, next) =>
{
var id = context.GetRouteValue("id"); // 可能为null
if (id?.ToString() == "admin")
{
// 尝试在这里注入DbContext会导致错误💥
}
await next();
});
这是很脆弱的,很容易出错——尤其是当路由发生变化时。
使用此表作为选择逻辑放置位置的参考指南。
💡 当你需要作用域服务时,注册IMiddleware
builder.Services.AddTransient<MyMiddleware>();
app.UseMiddleware<MyMiddleware>();
💡 避免基于URL片段或查询字符串控制流程 应该使用路由或操作过滤器。
💡 组合小型、单一职责的中间件 将中间件分解为原子单元,例如:
这会使你的管道保持清晰且易于调试。
中间件是ASP.NET Core请求生命周期中一个强大的早期钩子,但它并非适用于所有情况。
使用中间件的场景:
避免使用中间件的场景:
构建清晰的管道,而不是逻辑块。让过滤器和控制器去做它们设计用来做的事情。