Implement Rate Limiting In ASP.NET Core Web API

Introduction

In this article, you are going to explore rate limiting and how to implement the rate limiting in ASP.NET Core Web API.

What is Rate Limiting?

If you are creating the API, it can be used by many clients. Sometimes some of the clients consume the API frequently without any limit. But if you want to limit consuming the API for a particular client within a certain time, then you can achieve it by Rate Limiting.

Rate Limiting is the process of controlling the number of requests for a resource within a specific time window. Each unique user/IP address/client will have a limitation on the number of requests to an API endpoint.

Why do we need Rate Limiting?

  • Rate Limiting helps us to protect against malicious bot attacks. For example, a hacker can use bots to make repeated requests to an API endpoint. Due to the number of repeated requests, resulting in will be service unavailable for others. This is called as the Denial of Service (DoS) attack. So, the rate limiting helps us from the DoS attack.
  • Another use of the rate limiting is to regulate traffic to the API.

We can implement the rate limiting using below ways,

  • Using a Custom Middleware
  • Using the AspNetCoreRateLimit NuGet Package

In this article, we are going to implement using the AspNetCoreRateLimit NuGet Package.

Implement Rate Limiting in the ASP.NET Core using the AspNetCoreRateLimit NuGet Package 

Prerequisites

  • Visual Studio 2019 or Visual Studio 2022

Follow the below steps to create the ASP.NET Web API using Visual Studio 2019.

Step 1

Open Visual Studio 2019, click Create a new project.

Implement Rate Limiting in ASP.NET Core Web API

Step 2

Select ASP.NET Core Web Application project template and click Next.

Step 3

Enter the project name as RateLimiting_Using_AspNetCoreRateLimitPackage and Click Next.

Step 4

Select .NET Core 3.1 and Empty project and Click Create.

Step 5

Install the AspNetCoreRateLimit NuGet Package to implement the Rate Limiting.

The AspNetCoreRateLimit NuGet package is used to implement the rate limiting features directly in the ASP.NET Core pipeline. The AspNetCoreRateLimit package contains an IpRateLimitMiddleware and a ClientRateLimitMiddleware to support the IP Address and client-key limit respectively. 

In this example, we are going to use IpRateLimitMiddleware for limiting the IP address.

Step 6

Create Employee controller.

EmployeeController.cs

[Route("employee")]
[ApiController]
public class EmployeeController : ControllerBase
{
    
    [HttpGet("getAllEmployees")]
    [Produces("application/json")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public IEnumerable<Employee> GetAllEmployees()
    {
        return GetEmployeesDeatils();
    }

    [HttpGet("getEmployeeById/{id}")]
    [Produces("application/json")]
    public Employee GetEmployeeById(int id)
    {
        return GetEmployeesDeatils().Find(e => e.Id == id);
    }    

    private List<Employee> GetEmployeesDeatils()
    {
        return new List<Employee>()
        {
            new Employee()
            {
                Id = 1,
                FirstName= "Test",
                LastName = "Name",
                EmailId ="[email protected]"
            },
            new Employee()
            {
                Id = 2,
                FirstName= "Test",
                LastName = "Name1",
                EmailId ="[email protected]"
            }
        };
    }
}

The employee API has two endpoints, which are getting all employee details (getAllEmployees) and get particular employee detail(getEmployeeById). We are going to apply rate limit on getAllEmployees endpoint.

Step 7

To configure the rate limiting to use in-memory persistence, add the following lines to the ConfigureServices() method in Startup class.

services.AddMemoryCache();
services.Configure<IpRateLimitOptions>(options =>
{
    options.EnableEndpointRateLimiting = true;
    options.StackBlockedRequests = false;
    options.HttpStatusCode = 429;
    options.RealIpHeader = "X-Real-IP";
    options.ClientIdHeader = "X-ClientId";
    options.GeneralRules = new List<RateLimitRule>
        {
            new RateLimitRule
            {
                Endpoint = "GET:/employee/getAllEmployees",
                Period = "10s",
                Limit = 2,
            }
        };
});
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
services.AddInMemoryRateLimiting();

Add the following lines to the Configure() method in Startup class.

app.UseIpRateLimiting();

In the above code, the option EnableEndpointRateLimiting is set as true to ensure that limit is applied to specific endpoints (here employee/getAllEmployees) rather than all endpoints. If EnableEndpointRateLimiting is set to false then the limits will apply globally and only rules that have as endpoint * will apply.

The RealIpHeader is used to extract the client IP when your Kestrel server is behind a reverse proxy, if your proxy uses a different header, then X-Real-IP uses this option to set it up.

The ClientIdHeader is used to extract the client id for whitelisting, if a client id is present in this header and matches a value specified in ClientWhitelist then no rate limits are applied.

The rate limiting rules are set in the GeneralRules section. In the above example, the rule specifies that for the endpoint /employee/getAllEmployees with an HTTP verb GET, allow only 2 requests in a time window of 10 seconds.

Endpoint format is {HTTP_Verb}:{PATH}, you can target any HTTP verb by using the Asterix (*) symbol.

Period format is {INT}{PERIOD_TYPE}, you can use one of the following period types: s, m, h, d.

Limit format is {LONG}.

Example

{
   "Endpoint": "*",
   "Period": "10s",
   "Limit": 2
}

In the above example, the rule specifies that for all endpoints, allow only 2 requests in a time window of 10 seconds. If the request is from the same IP, consider 3 GET calls to getAllEmployees, the last call (3rd call) will get blocked. But if in the same second you call PUT in employee then the request will go through because it's a different endpoint.

Step 8

Press F5 to run the API locally and to launch the Swagger UI just hit the http://localhost:<port_number>/swagger/index.html URL in the browser. The Swagger UI for the above controller looks as follows, (Please look my other article about how to integrate the Swagger in ASP.NET Core).

Implement Rate Limiting in ASP.NET Core Web API

Execute the getAllEmployees endpoint. You will get below response. The API endpoint returns result along with three response headers, related to rate-limiting.

Implement Rate Limiting in ASP.NET Core Web API

  • x-rate-limit-limit - It shows the rate limit time window
  • x-rate-limit-remaining - The remaining number of requests for the API endpoint for that particular time window. 
  • x-rate-limit-reset - It displays the time stamp for resetting the limit rules.

If you hit the above endpoint more than 2 times within 10 seconds (rate-limiting rules are violated) then failure response (status code is 429) will be returned. The headers has retry-after: 7 which means the client to retry after 7 seconds to avoid the violation of rate-limiting rules.

Implement Rate Limiting in ASP.NET Core Web API

Summary

In this article, you have learned the following topics,

  • What is Rate Limiting?
  • Why do we need Rate Limiting?
  • Implement Rate Limiting in the ASP.NET Core using the AspNetCoreRateLimit NuGet Package.

Visit the GitHub repository to download the source code for this article.