如何为特定API端点覆盖全局Rate Limiter?
问题:ASP.NET Core 7 限流策略覆盖冲突
我在为应用设置Rate Limiting时遇到以下问题:
- 希望启用全局限流(覆盖所有端点)
- 但需要为特定API端点禁用全局限流,转而应用更宽松的自定义限流策略
目前我同时配置了全局限流中间件和API专属限流中间件,但两者会同时生效,专属策略无法覆盖全局限流。相关代码如下:
builder.Services.AddRateLimiter(limiterOptions => { limiterOptions.OnRejected = (context, cancellationToken) => { if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter)) { context.HttpContext.Response.Headers.RetryAfter = ((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo); } context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; GetUserEndPoint(context.HttpContext)); return new ValueTask(); }; limiterOptions.AddPolicy(userPolicyName, context => { var username = "anonymous user"; if (context.User.Identity?.IsAuthenticated is true) { //await context.GetTokenAsync(JwtBearerDefaults, AuthenticationScheme, "access_token"); username = context.Request.Headers.Authorization!; } return RateLimitPartition.GetFixedWindowLimiter(username, _ => new FixedWindowRateLimiterOptions { PermitLimit = myOptions.TokenLimit2, Window = TimeSpan.FromSeconds(myOptions.Window), }); }); limiterOptions.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(context => { var test = context.Request.Host; IPAddress? remoteIpAddress = context.Connection.RemoteIpAddress; if (IPAddress.IsLoopback(remoteIpAddress!)) { return RateLimitPartition.GetSlidingWindowLimiter (remoteIpAddress!, _ => new SlidingWindowRateLimiterOptions { PermitLimit = myOptions.PermitLimit, //QueueProcessingOrder = QueueProcessingOrder.OldestFirst, //QueueLimit = myOptions.QueueLimit, Window = TimeSpan.FromSeconds(myOptions.Window), SegmentsPerWindow = myOptions.SegmentsPerWindow }); } return RateLimitPartition.GetNoLimiter(IPAddress.Loopback); });
解决方案
在ASP.NET Core 7的限流体系中,全局限流器会默认对所有端点生效,若要让特定端点绕过全局策略并使用自定义策略,需要通过端点路由配置显式指定策略优先级,同时标记这些端点跳过全局限流。
修改步骤
将全局限流规则封装为命名策略
移除直接设置GlobalLimiter的方式,把原全局规则转为一个命名策略,这样可以灵活控制哪些端点应用它。在端点路由中区分策略应用
- 对需要宽松自定义策略的API端点,显式绑定专属策略,并禁用全局限流。
- 对其他所有端点,统一应用全局限流策略。
修改后的完整代码
服务配置部分(Program.cs)
builder.Services.AddRateLimiter(limiterOptions => { limiterOptions.OnRejected = (context, cancellationToken) => { if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter)) { context.HttpContext.Response.Headers.RetryAfter = ((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo); } context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; GetUserEndPoint(context.HttpContext); // 修正原代码多余的括号 return new ValueTask(); }; // 定义用户专属宽松限流策略 const string userCustomPolicy = "UserCustomPolicy"; limiterOptions.AddPolicy(userCustomPolicy, context => { var username = "anonymous user"; if (context.User.Identity?.IsAuthenticated is true) { username = context.Request.Headers.Authorization!; } return RateLimitPartition.GetFixedWindowLimiter(username, _ => new FixedWindowRateLimiterOptions { PermitLimit = myOptions.TokenLimit2, Window = TimeSpan.FromSeconds(myOptions.Window), }); }); // 定义全局IP限流策略(替代原GlobalLimiter) const string globalIpPolicy = "GlobalIpPolicy"; limiterOptions.AddPolicy(globalIpPolicy, context => { IPAddress? remoteIpAddress = context.Connection.RemoteIpAddress; if (IPAddress.IsLoopback(remoteIpAddress!)) { return RateLimitPartition.GetSlidingWindowLimiter(remoteIpAddress!, _ => new SlidingWindowRateLimiterOptions { PermitLimit = myOptions.PermitLimit, Window = TimeSpan.FromSeconds(myOptions.Window), SegmentsPerWindow = myOptions.SegmentsPerWindow }); } return RateLimitPartition.GetNoLimiter(IPAddress.Loopback); }); });
端点路由配置部分(Program.cs)
app.UseRateLimiter(); // 配置需要宽松策略的API端点:禁用全局限流,仅应用专属策略 app.MapControllers() .RequireRateLimiting("UserCustomPolicy") .DisableRateLimiting(false); // 对其他所有端点应用全局限流策略 app.MapGet("/", () => "Hello World!") .RequireRateLimiting("GlobalIpPolicy"); // MVC视图路由配置示例 app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}") .RequireRateLimiting("GlobalIpPolicy");
关键说明
DisableRateLimiting(false):该参数设为false表示禁用全局限流,让端点只遵循显式指定的RequireRateLimiting策略。- 若有多个需要宽松策略的端点,可以通过路由分组统一配置:
app.MapGroup("/api/relaxed") .MapControllers() .RequireRateLimiting("UserCustomPolicy") .DisableRateLimiting(false);
内容的提问来源于stack exchange,提问作者change world
相关产品推荐
相关产品推荐

