ASP.NET Core - Rate limiting
Rate limiting is a technique used mainly with APIs to control and limit the number of requests that clients can make to the API in a defined time. It belongs to the Resiliences family of paterns, which ensure that an API remains stable and available to all users, preventing misuse or overuse that could compromise its performance.
In the past, ASP.NET Core used the AspNetCoreRateLimit library for rate limiting. As of .NET 7, ASP.NET Core rate limiting is available directly within the framework.
Choose one of the available rate limiting algorithms:
- Fixed window
- Sliding window
- Token bucket
- Concurrency
๐ You can find the individual methods explained in detail directly in documentation.
Register a policy first:
builder.Services.AddRateLimiter(limiter =>
{
// ๐ Define the status code to be returned when the request is rejected.
limiter.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
// ๐ Define the policy.
limiter.AddFixedWindowLimiter(policyName: "products", options =>
{
options.PermitLimit = 10;
options.Window = TimeSpan.FromSeconds(1);
// ๐ If you want to use a queue to process requests that exceed the limit, you can set the following options.
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = 4;
});
});
Then add the middleware to the pipeline:
app.UseRateLimiter();
And use the policy when defining the endpoint:
app.MapGet("/products", () =>
{
return new string[] { "product1", "product2", "product3", "product4" };
}).RequireRateLimiting("products"); // ๐ Add the policy to endpoint.
If we have defined a policy on a whole group of endpoints, you can then disable rate limiting for a specific endpoint:
var group = app.MapGroup("/api/products")
.RequireRateLimiting("products");
group.MapGet("/", () =>
{
return new string[] { "product1", "product2", "product3", "product4" };
}).DisableRateLimiting(); // ๐ Disable the policy for the endpoint.
For controllers, you can use the [EnableRateLimiting(...)]
and [DisableRateLimiting]
attributes.
[EnableRateLimiting("products")]
public class ProductsController : Controller
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "product1", "product2", "product3", "product4" };
}
[DisableRateLimiting]
[HttpGet("{id}")]
public string Get(int id)
{
return "product";
}
}
Multitenancy
If you are building a multi-tenant system, you probably want to have rate limiting for each tenant. For example, you want a tenant to be able to make 10 requests per second. Alternatively, you want to have different limits for different tenants. This can be achieved using the RateLimitPartition
class.
builder.Services.AddRateLimiter(limiter =>
{
// ๐ Define custom policy.
limiter.AddPolicy("fixed-by-tenant",
context => RateLimitPartition
.GetFixedWindowLimiter(
context.Request.Headers["tenant-id"].First(), // ๐ Get tenant id
_ => new FixedWindowRateLimiterOptions
{
Window = TimeSpan.FromSeconds(1),
PermitLimit = 10
}));
// ๐ If you want different policy per tenant
limiter.AddPolicy("fixed-by-tenant-2",
context => RateLimitPartition
.GetFixedWindowLimiter(
context.Request.Headers["tenant-id"].First(), // ๐ Get tenant id
tenantId => GetOptionsByTenant(tenantId)));
// ๐ Get options by tenan from your configuration
});