Implementing API Rate Limiting in .NET Web APIs

Throttling is the process of limiting the rate at which requests are made to an API. In a .NET Core API, you can limit API calls by implementing throttling.

There are several ways to implement throttling in a .NET Core API. One approach is to use a middleware component that sits between the client and the API. This middleware can inspect incoming requests and determine whether they should be allowed to proceed or be throttled.

To add a limit rating I will use the following:

  1. C#
  2. .NET 7 (current latest version)
  3. Visual Studio – Community Edition

I have created an empty ASP.NET Web API project using .NET 7 and published it to my Github repository, Initial branch.

Install Nuget Package

The first step is going to be installing AspNetCoreRateLimit NuGet package. After you install the NuGet package you have access to rate-limiting middleware.

Installing AspNetCoreRateLimit package

Now, that you have installed the package, it is time to set up the IpRateLimitOptions in the middleware.

Setting Up IpRateLimitOptions

To configure the rate limit you need to go to Program.cs and before the var app = builder.Build(); you need to add the IpRateLimitOptions configuration.

I will add a simple configuration, like below:

builder.Services.Configure<IpRateLimitOptions>(options =>
{
    options.GeneralRules = new List<RateLimitRule>
    {
        new RateLimitRule
        {
            Endpoint = "*",
            Period = "10s",
            Limit = 2,
        }
    };
});

The code above configures the rate-limiting options for an ASP.NET Core application. It uses the Configure method to specify the IpRateLimitOptions class, which is part of the AspNetCoreRateLimit package. The IpRateLimitOptions class contains properties for configuring rate limiting for IP addresses.

The GeneralRules property is set to a new list of RateLimitRule objects. The RateLimitRule class represents a rate limit rule that applies to a specific endpoint. In this case, the Endpoint property is set to "*", which means that the rate limit rule applies to all endpoints.

The Period property is set to "10s", which specifies the time period over which the rate limit applies. In this case, the rate limit applies over a period of 10 seconds.

The Limit property is set to 2, which specifies the maximum number of requests allowed during the time period specified by the Period property. In this case, the rate limit allows a maximum of 2 requests every 10 seconds.

Dependencies

For the above configuration to work, you need to also add a service configuration:

builder.Services.AddInMemoryRateLimiting();

But, the line above does have a dependency on some other services, which you need to configure as well:

builder.Services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
builder.Services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
builder.Services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();

Here is what each line of code does:

  • builder.Services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>(): This line of code registers the MemoryCacheIpPolicyStore class as a singleton service for storing IP address policies. This service is used to store the policies that determine whether a given IP address should be rate-limited or not.
  • builder.Services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>(): This line of code registers the MemoryCacheRateLimitCounterStore class as a singleton service for storing rate limit counters. This service is used to store the number of requests made by each IP address during a given time period.
  • builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(): This line of code registers the RateLimitConfiguration class as a singleton service for configuring the rate limit settings. This service is used to configure the general and endpoint-specific rate limit rules for the application.
  • builder.Services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>(): This line of code registers the AsyncKeyLockProcessingStrategy class as a singleton service for processing rate-limiting requests. This service is used to handle concurrent rate limit requests, ensuring that only one request is processed at a time.

The Program.cs currently looks like below:

using AspNetCoreRateLimit;

//Some code

builder.Services.Configure<IpRateLimitOptions>(options =>
{
    options.GeneralRules = new List<RateLimitRule>
    {
        new RateLimitRule
        {
            Endpoint = "*",
            Period = "10s",
            Limit = 2,
        }
    };
});

builder.Services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
builder.Services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
builder.Services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
builder.Services.AddInMemoryRateLimiting();

var app = builder.Build();

// Some code

If you run the app with the code that you have above, you will get an error that looks like below:

Exception

The full details of the error look like below:

Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: AspNetCoreRateLimit.IIpPolicyStore Lifetime: Singleton ImplementationType: AspNetCoreRateLimit.MemoryCacheIpPolicyStore': Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache' while attempting to activate 'AspNetCoreRateLimit.MemoryCacheIpPolicyStore'.) (Error while validating the service descriptor 'ServiceType: AspNetCoreRateLimit.IRateLimitCounterStore Lifetime: Singleton ImplementationType: AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore': Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache' while attempting to activate 'AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore'.) (Error while validating the service descriptor 'ServiceType: AspNetCoreRateLimit.IProcessingStrategy Lifetime: Singleton ImplementationType: AspNetCoreRateLimit.AsyncKeyLockProcessingStrategy': Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache' while attempting to activate 'AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore'.) (Error while validating the service descriptor 'ServiceType: AspNetCoreRateLimit.IIpPolicyStore Lifetime: Singleton ImplementationType: AspNetCoreRateLimit.MemoryCacheIpPolicyStore': Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache' while attempting to activate 'AspNetCoreRateLimit.MemoryCacheIpPolicyStore'.) (Error while validating the service descriptor 'ServiceType: AspNetCoreRateLimit.IClientPolicyStore Lifetime: Singleton ImplementationType: AspNetCoreRateLimit.MemoryCacheClientPolicyStore': Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache' while attempting to activate 'AspNetCoreRateLimit.MemoryCacheClientPolicyStore'.) (Error while validating the service descriptor 'ServiceType: AspNetCoreRateLimit.IRateLimitCounterStore Lifetime: Singleton ImplementationType: AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore': Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache' while attempting to activate 'AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore'.) (Error while validating the service descriptor 'ServiceType: AspNetCoreRateLimit.IProcessingStrategy Lifetime: Singleton ImplementationType: AspNetCoreRateLimit.AsyncKeyLockProcessingStrategy': Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache' while attempting to activate 'AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore'.)

The error above as you can see is related to Microsoft.Extensions.Caching.Memory.IMemoryCache and to fix this error, you need to also configure MemoryCache, for that all you need to do is you need to add the line below:

builder.Services.AddMemoryCache();

This line needs to be added before this line:

builder.Services.Configure<IpRateLimitOptions>(...);
HTTP Request Pipeline

There is one last step for everything to be configured as expected, and that is to add IpRateLimiting as part of the HTTP request pipeline.

For that, you can use the following line:

app.UseIpRateLimiting();

Now, the full Program.cs file looks like this:

using AspNetCoreRateLimit;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddMemoryCache();
builder.Services.Configure<IpRateLimitOptions>(options =>
{
    options.GeneralRules = new List<RateLimitRule>
    {
        new RateLimitRule
        {
            Endpoint = "*",
            Period = "10s",
            Limit = 2,
        }
    };
});

builder.Services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
builder.Services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
builder.Services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
builder.Services.AddInMemoryRateLimiting();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseIpRateLimiting();
app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
Testing

Everything is set up. It is time to test what we have set up. If you run the project you will get the Swagger UI which looks like below

Swagger UI

Let us call the WeatherForecast API endpoint which is the default API endpoint in a .NET API project. If you call it more than 2, you are going to get the following error, which means that the rate limit set up has worked as expected.

API calls quota exceeded! maximum admitted 2 per 10s.

You can find the final state of the code in this GitHub repository.

Image by Freepik


Enjoyed this post? Subscribe to my YouTube channel for more great content. Your support is much appreciated. Thank you!


Check out my Udemy profile for more great content and exclusive learning resources! Thank you for your support.
Ervis Trupja - Udemy



Enjoyed this blog post? Share it with your friends and help spread the word! Don't keep all this knowledge to yourself.

One thought on “Implementing API Rate Limiting in .NET Web APIs

Comments are closed.