top of page

How to implement rate limiting in ASP.NET Core

Updated: Mar 18, 2023

What is Rate Limiting?

Rate limiting is a technique used to limit the number of requests that a client can make to a web application or API within a specific time period. In ASP.NET Core, rate limiting can be implemented using either middleware or action filters. The goal of rate limiting is to protect the web application or API from abuse, such as Denial of Service (DoS) attacks, by preventing clients from making an excessive number of requests in a short period of time.


Rate limiting works by enforcing a limit on the number of requests that a client can make within a specified time frame. For example, a rate-limiting policy might allow a maximum of 10 requests per second from a single IP address. If the client exceeds this limit, the web application or API will return an error response, such as a 429 Too Many Requests status code.


Why do we use Rate Limiting?

There are several reasons why rate limiting is important in ASP.NET Core applications:

  1. Preventing DoS attacks: A DoS attack is a type of cyber attack where a large number of requests are sent to a web application or API with the goal of overwhelming the server and causing it to crash. Rate limiting can prevent these types of attacks by limiting the number of requests that a client can make within a given time period.

  2. Protecting limited resources: Some web applications or APIs may have limited resources, such as bandwidth or database connections. Rate limiting can prevent clients from monopolizing these resources and ensure that they are available to all clients.

  3. Maintaining service level agreements (SLAs): Some web applications or APIs may have SLAs that require a certain level of performance or availability. Rate limiting can prevent clients from exceeding the limits specified in the SLA and ensure that the web application or API meets its performance and availability requirements.


Rate limiting is an important technique used to prevent abuse of an API or web service by limiting the number of requests that can be made in a given period of time. In ASP.NET Core, there are two approaches to implementing rate limiting: using middleware and using action filters. Both approaches involve setting up a counter to keep track of the number of requests made by each client, and then checking this counter on each request to determine whether the client has exceeded the allowed limit.


Using Middleware:


STEP 1: Install the AspNetCore.RateLimit package using NuGet.


STEP 2: Add the following configuration to the ConfigureServices method in Startup.cs:

services.AddRateLimiting();

This configures the rate-limiting services and sets up the default options.


STEP 3: Add the following configuration to the Configure method in Startup.cs:

app.UseRateLimiting();

This adds the rate-limiting middleware to the pipeline.


STEP 4: Configure the rate-limiting options in appsettings.json:

"RateLimiting": 
{
    "ClientIdHeader": "X-ClientId",
    "QuotaExceededResponseStatusCode": 429,
    "ClientRules": [{"ClientId": "*","Limit": 100,"Period": "1m"}]
}

This configuration specifies the header to use for the client ID, the response status code to return when the quota is exceeded, and the rules for each client. In this example, the rule specifies a limit of 100 requests per minute for all clients.


STEP 5: Add the following middleware to the pipeline in the Configure method in Startup.cs:

app.Use(async (context, next) =>
{
    var rateLimitCounter = context.RequestServices.GetRequiredService<IRateLimitCounter>();
    var clientId = context.Request.Headers["X-ClientId"].FirstOrDefault();
    var result = await rateLimitCounter.CheckRateLimitAsync(clientId);
    if (!result.IsWithinLimit)
    {
        context.Response.StatusCode = 429;
        await context.Response.WriteAsync($"Rate limit exceeded. Try again in {result.RetryAfterSeconds} seconds.");
        return;
    }
    await next.Invoke();
});

This middleware checks the rate limit for each request by getting the rate limit counter from the dependency injection container, extracting the client ID from the request headers, and checking the rate limit for that client. If the client has exceeded the limit, the middleware returns a 429 status code and a message indicating how long the client must wait before trying again.


Using Action Filters:


STEP 1: Create a new class that implements the IActionFilter interface:

public class RateLimitingAttribute : Attribute, IActionFilter
{
    public async void OnActionExecuting(ActionExecutingContext context)
    {
        var rateLimitCounter = context.HttpContext.RequestServices.GetRequiredService<IRateLimitCounter>();
        var clientId = context.HttpContext.Request.Headers["X-ClientId"].FirstOrDefault();
        var result = await rateLimitCounter.CheckRateLimitAsync(clientId);
        if (!result.IsWithinLimit)
        {
            context.Result = new StatusCodeResult(429);
            context.HttpContext.Response.Headers.Add("Retry-After", result.RetryAfterSeconds.ToString());
            await context.HttpContext.Response.WriteAsync($"Rate limit exceeded. Try again in {result.RetryAfterSeconds} seconds.");
            return;
        }
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
    }
}

This action filter checks the rate limit for each request by getting the rate limit counter from the dependency injection container, extracting the client ID from the request headers, and checking the rate limit for that client. If the client has exceeded the limit, the action filter sets the result to a 429 status code and adds the Retry-After header to indicate how long the client must wait before trying again.


STEP 2: Add the RateLimitingAttribute to the controller action that you want to rate limit:

[HttpGet]
[Route("api/test")]
[RateLimiting]
public IActionResult Test()
{
    return Ok();
}

This attribute tells ASP.NET Core to apply the rate-limiting action filter to the Test action.


STEP 3: Configure the rate-limiting options in appsettings.json:

"RateLimiting": 
{
    "ClientIdHeader": "X-ClientId",
    "QuotaExceededResponseStatusCode": 429,
    "ClientRules": [{"ClientId": "*","Limit": 100,"Period": "1m"}]
}

This configuration specifies the header to use for the client ID, the response status code to return when the quota is exceeded, and the rules for each client. In this example, the rule specifies a limit of 100 requests per minute for all clients.


By using either middleware or action filters, ASP.NET Core developers can easily implement rate limiting to protect their APIs or web services from abuse. Additionally, using configuration files such as appsettings.json allows developers to easily configure and fine-tune the rate-limiting rules for their specific use cases.

0 comments
bottom of page