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
});