EndpointFilter 必须链式注册到具体端点(如MapGet后),不能仅挂MapGroup;依赖需通过context.RequestServices获取;执行时机在参数绑定后、handler前,不可替代授权中间件但可做权限后置校验。

Minimal API 的 EndpointFilter 不是 MVC 的 ActionFilter,它不走过滤器链、不支持同步短路、不能在 handler 外提前终止请求——所有逻辑必须在 InvokeAsync 里显式控制是否调用 next。
为什么加了 AddEndpointFilter 却没生效?
最常见原因是注册位置错误或生命周期不匹配:
-
AddEndpointFilter必须链式挂在具体端点(如MapGet)之后,不能只挂到MapGroup上就以为整组都生效——MapGroup返回的是IEndpointConventionBuilder,它不直接注册 filter,得对每个子端点单独调用 - 用 lambda 写内联 filter 时,若内部依赖服务(如
ILogger),不能直接 new 实例;必须用context.RequestServices.GetRequiredService<T>()拿,否则会报NullReferenceException - 自定义类实现
IEndpointFilter时,构造函数参数必须能被 DI 容器解析;如果写了public MyFilter(IOptions<Config> opts, ILogger<MyFilter> log),就得确保AddOptions<Config>()和AddLogging()已在builder.Services中注册
EndpointFilter 能否替代授权中间件?
不能完全替代,但可做细粒度补充:
-
RequireAuthorization()是全局策略入口,靠IAuthorizationService执行策略评估,失败直接返回 401/403 -
EndpointFilter在授权通过后才执行,适合做“权限后置校验”,比如检查用户是否有权操作某条具体数据:var userId = context.HttpContext.User.FindFirst("sub")?.Value;+ 查询 DB 判断是否属该租户 - 想在 filter 里短路响应(如返回 403),必须手动写
return Results.Forbid();,且不能跳过后续 filter —— 因为next不调就没了,但已注册的其他 filter 也不会再运行
如何让 EndpointFilter 支持异步前置校验并自动重试?
可以,但要注意 await 顺序和上下文捕获:
- 不要在
InvokeAsync开头就await一个可能失败的远程调用(如 Redis 检查令牌),因为一旦异常未 catch,整个请求就 500,Swagger 里也看不到明确错误码 - 推荐结构:
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) { var token = context.HttpContext.Request.Headers.Authorization; if (!await IsValidTokenAsync(token)) { return Results.Unauthorized(); } try { return await next(context); } catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.ServiceUnavailable) { // 自动重试一次 await Task.Delay(200); return await next(context); } } - 注意:重试逻辑必须包裹在
try/catch里,且不能依赖context的任何可变状态(如HttpContext.Items可能已被清理)
用特性方式声明 EndpointFilter 时容易漏哪一步?
97% 的人漏掉 CreateFilter 方法里的服务解析方式:
- 自定义特性必须继承
EndpointFilterAttribute,不是随便起个名加[AttributeUsage]就行 -
CreateFilter返回的 filter 实例,如果要用 DI 服务,必须从context.ServiceProvider获取,而不是用new MyFilter()—— 后者绕过 DI,所有依赖都是 null - 正确写法示例:
public class AuthFilterAttribute : EndpointFilterAttribute { public override EndpointFilterFactory CreateFilter(EndpointFilterFactoryContext context) { return invocationContext => { var svc = invocationContext.ServiceProvider.GetRequiredService<IAuthService>(); return new AuthFilter(svc).InvokeAsync(invocationContext, _ => throw new NotSupportedException()); }; } }
真正难的不是写 filter,而是判断它该在哪一层介入:路由匹配后?授权后?模型绑定后?Minimal API 没有这些钩子命名,全靠你读源码确认 EndpointFilter 的实际执行时机——它夹在 endpoint 匹配完成、参数绑定完毕、handler 执行之前,仅此一处。


















