asp.net core 8+ 的 ratelimiter 中间件必须显式注册策略(如 addfixedwindowlimiter)并调用 useratelimiter("name") 绑定,否则完全不生效;滑动窗口原生不支持,需自行实现或借助 redis。

ASP.NET Core 8+ 的 RateLimiter 中间件必须显式注册策略并绑定到请求,否则完全不生效——不是“限流没起作用”,而是压根没进入限流逻辑。
固定窗口限流必须用 AddFixedWindowLimiter 注册策略
原生限流中间件不会自动启用任何策略。只调用 AddRateLimiter() 而不指定具体限流器类型,运行时会抛出 InvalidOperationException: No rate limiter policy is configured。
-
AddFixedWindowLimiter("fixed", options => { ... })是唯一能直接注册的内置窗口类型;AddSlidingWindowLimiter根本不存在于 .NET 8/9 API 中 -
Window必须是TimeSpan,推荐写成TimeSpan.FromMinutes(1),避免硬编码毫秒数(如60000)导致语义模糊或单位错误 -
PermitLimit表示该窗口内允许的总请求数,不是并发数;它不控制连接数或线程数,只计数请求抵达次数 -
QueueLimit = 0表示不排队,超限时直接返回429 Too Many Requests;设为正数会启用内存队列,但高并发下可能 OOM
UseRateLimiter() 必须放在正确位置且指定策略名
中间件注册顺序和策略名匹配是两个最常被忽略的硬性条件。漏掉任一环节,限流就形同虚设。
-
UseRateLimiter()必须在UseRouting()之后、UseEndpoints()或UseAuthorization()之前调用;放错位置会导致HttpContext.GetEndpoint()返回 null,策略无法解析 - 全局启用需传入策略名:例如
app.UseRateLimiter("fixed"),名称必须与AddFixedWindowLimiter("fixed", ...)中第一个参数**完全一致**(大小写敏感) - 若想按端点差异化限流,不能靠全局
UseRateLimiter(),而要用[EnableRateLimiting("per-api")]特性标记 Controller 或 Action,并确保该策略已注册 - 未加特性的端点默认不受限——这不是 bug,是设计行为;别误以为“没生效”,其实是根本没配策略
滑动窗口限流在原生中间件中不可用
.NET 原生 RateLimiter 库目前(截至 2026 年 4 月)没有提供开箱即用的滑动窗口实现。所有声称“支持滑动窗口”的文档或示例,基本都混淆了抽象基类和具体实现。
-
SlidingWindowRateLimiter类型仅作为抽象基类存在,未公开构造函数,也无法通过扩展方法注册 - 自行实现需维护多个时间桶(如 10 个 6 秒桶)、处理跨桶聚合、保证多线程安全——极易引入时钟漂移、计数丢失或内存泄漏
- 生产环境更可行的路径是:用
StackExchange.Redis+ Lua 脚本实现分布式滑动窗口,或引入实验性包Microsoft.Extensions.Resilience(注意其 API 尚不稳定) - 若项目已用
AspNetCoreRateLimit,请注意该库已于 2025 年归档,不再维护,不建议新项目接入
真正容易踩坑的地方不在代码怎么写,而在于误判“策略已注册=限流已生效”。实际调试时,先确认日志里有没有 RateLimiterMiddleware 的 trace 输出,再检查 HttpContext.GetEndpoint()?.Metadata.GetMetadata<enableratelimitingattribute>()</enableratelimitingattribute> 是否非空——这才是判断策略是否触达端点的可靠依据。











