Microsoft.AspNetCore.RateLimiting RateLimiterPolicy GetPartitionAsync
See original GitHub issueBackground and Motivation
I want to save API rate limits in the database and have the possibility of changing them at runtime (eg: based on the customer’s subscription/licensing/etc).
The problem is that IRateLimiterPolicy doesn’t have an async method
public class ClientIdRateLimiterPolicy : IRateLimiterPolicy<string>
{
private readonly IServiceProvider _serviceProvider;
public ClientIdRateLimiterPolicy(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public RateLimitPartition<string> GetPartition(HttpContext httpContext)
{
var clientId = httpContext.Request.Headers["X-ClientId"].ToString();
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<SampleDbContext>();
var rateLimit = dbContext.Clients.Where(x => x.Identifier == clientId).Select(x => x.RateLimit).FirstOrDefault();
return RateLimitPartition.GetConcurrencyRateLimiter(clientId, key => new ConcurrencyRateLimiterOptions
{
PermitLimit = rateLimit?.PermitLimit ?? 1
});
}
}
Proposed API
public interface IRateLimiterPolicy<TPartitionKey>
{
Func<OnRejectedContext, CancellationToken, ValueTask>? OnRejected { get; }
- RateLimitPartition<TPartitionKey> GetPartition(HttpContext httpContext);
+ ValueTask<RateLimitPartition<TPartitionKey>> GetPartitionAsync(HttpContext httpContext);
}
Usage Examples
public class ClientIdRateLimiterPolicy : IRateLimiterPolicy<string>
{
private readonly IServiceProvider _serviceProvider;
public ClientIdRateLimiterPolicy(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async ValueTask<RateLimitPartition<string>> GetPartitionAsync(HttpContext httpContext)
{
var clientId = httpContext.Request.Headers["X-ClientId"].ToString();
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<SampleDbContext>();
var rateLimit = await redisCache.GetAsync(clientId);
var rateLimit = await dbContext.Clients.Where(x => x.Identifier == clientId).Select(x => x.RateLimit).FirstOrDefaultAsync();
return RateLimitPartition.GetConcurrencyRateLimiter(clientId, key => new ConcurrencyRateLimiterOptions
{
PermitLimit = rateLimit?.PermitLimit ?? 1
});
}
}
Risks
Find a way w/o breaking changes
Issue Analytics
- State:
- Created 9 months ago
- Reactions:1
- Comments:6 (5 by maintainers)
Top Results From Across the Web
Rate limiting middleware in ASP.NET Core
RateLimiting middleware provides rate limiting middleware. Apps configure rate limiting policies and then attach the policies to endpoints. Apps ...
Read more >Announcing Rate Limiting for .NET - .NET Blog
Concurrency limiter limits how many concurrent requests can access a resource. If your limit is 10, then 10 requests can access a resource...
Read more >Rate Limiting
Rate Limiter policies can be specified per route via RouteConfig.RateLimiterPolicy and can be bound from the Routes sections of the config file. As...
Read more >Microsoft.AspNetCore.RateLimiting Namespace
Metadata that provides endpoint-specific request rate limiting. OnRejectedContext. Holds state needed for the OnRejected callback in the RateLimitingMiddleware.
Read more >RateLimiterOptions Class
Gets or sets the global PartitionedRateLimiter<TResource> that will be applied on all requests. The global limiter will be executed first, followed by the ......
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

This started as an edit to my previous comment, but it became big enough to deserve it’s own comment for the folks following along via email.
I think your best bet for now is to read the rate-limit asynchronously before calling into the rate limiting middleware. You can look for the
EnableRateLimitingAttributeto make sure you’re not doing this unnecessarily when the endpoint doesn’t have your policy defined. This attribute should be present in the endpoint metadata whether or not it was configured as an attribute on the method or via theRequireRateLimiting()extension method.The downside of this logic is that it will get run even if the middleware only needed the key and not the rate limiter instance from the factory. The originally proposed API has the same downside though.
Yet another alternative might be to add something like
Func<TKey, HttpContext, ValueTask<RateLimiter>>? RateLimitPartition<TKey>.AsyncFactoryand updating our callers to use that if it’s set. We’d also need correspondingRateLimitPartition.GetConcurrencyLimiteroverloads or similar that took async factories to go with it.I believe that the option to make an async call in cases like this should be added to the framework’s APIs, the workaround is just a workaround