In this tutorial, we are going to discuss about Rate limiting in .NET 6.0 and see the different ways of implementing it.
What is Rate Limiting?
Rate Limiting is the process of limiting how much a resource can be accessed within a specific time window. The limitation on the number of requests to each API endpoint can be applied to each unique user/IP address/client.
Purpose of Rate Limiting?
Generate Revenue
Public APIs use rate limiting for commercial purposes. In market, there are a lot of public APIs available to generate revenues. I would say, a certain subscription amount needs to be paid for leveraging the APIs.
Prevent malicious Attack (DoS attack)
For an instance, a hacker can use bots (I hope all would be familiar about bots) to make repeated calls to an API endpoint to let the service become unable to anyone else. This is known as Denial of Service (DoS) attack.
Regulate traffic to APIs based on infrastructure availability
This is more applicable to Cloud providers who would utilize “Pay as you go” IaaS (Infrastructure as a Service) strategy.
Note
ASP.NET Core does not support Rate Limiting out of the box in .NET 6.0. ASP.NET Core framework provides HTTP Middleware extensibility options for this purpose.
The entire source code is available in the Repository
Here, we are going to check two approaches.
- Using custom middleware approach
- Use the Nuget package AspNetCoreRateLimit
Let us look at the demo API application to understand the implementation.
Tools which I have used for this Demo
- VS 2022 Community Edition -Version 17.3.0 Preview 6.0
- .NET 6.0
- Swagger/Post Man
Demo Web API
In our Demo, we will have two action methods – one will return the student details by Student Id and the another will return the entire students. As this article focuses on the Rate Limiting, I’m keeping our project structure very simple and short.
I’m using in-memory dictionary for simplicity as a persistence store for students. For enterprise applications, obviously we would be using relational or non-relational data sources.
The Controller contains the below two action methods
[HttpGet("id")]
public IActionResult GetStudentById(Guid id) {
var student = _studentRepository.GetStudentById(id);
return student is not null ? Ok(student) : NotFound();
}
[HttpGet("")]
public IActionResult GetAllStudents() {
return Ok(_studentRepository.GetAllStudents());
}
Depending on the requirements, the API may apply throttling to all the endpoints or certain specific endpoint. To achieve this, I would say, the best approach would be to create a decorator and use it.
Custom Middleware Approach
Let us go ahead and create a Decorator.
namespace CustomMiddleware.Decorators {
[AttributeUsage(AttributeTargets.Method)]
public class LimitRequest: Attribute {
public int TimeWindow {
get;
set;
}
public int MaxRequests {
get;
set;
}
}
}
This attribute applies only to methods. Two properties in this attribute indicates the maximum requests allowed within a specific time window. This approach gives us the flexibility to configure the Rate limit attributes.
Apply Rate Limit to the endpoints.
Let us apply the LimitRequest attribute to the endpoints in StudentController. The updated endpoints are below
[HttpGet("id")]
[LimitRequest(MaxRequests = 2, TimeWindow = 5)]
public IActionResult GetStudentById(Guid id) {
var student = _studentRepository.GetStudentById(id);
return student is not null ? Ok(student) : NotFound();
}
[HttpGet("")]
[LimitRequest(MaxRequests = 2, TimeWindow = 5)]
public IActionResult GetAllStudents() {
return Ok(_studentRepository.GetAllStudents());
}
Here we have configured to allow maximum of two requests for window of five seconds. Whenever there is a third request within the windows of five seconds, there will not be any successful response.
Now let us go ahead and create the custom middleware.
Custom Middleware to read LimitRequest attribute
Now we are going to create a middleware called RateLimitingMiddleware which contains the logic for rate limiting.
The source code is available in the repository.
Logic for Rate limiting as below,
public async Task InvokeAsync(HttpContext context) {
var endpoint = context.GetEndpoint();
// read the LimitRequest attribute from the endpoint
var rateLimitDecorator = endpoint?.Metadata.GetMetadata < LimitRequest > ();
if (rateLimitDecorator is null) {
await _next(context);
return;
}
var key = GenerateClientKey(context);
var _clientStatistics = GetClientStatisticsByKey(key).Result;
// Check whether the request violates the rate limit policy
if (_clientStatistics != null && DateTime.Now < _clientStatistics.LastSuccessfulResponseTime.AddSeconds(rateLimitDecorator.TimeWindow) && _clientStatistics.NumberofRequestsCompletedSuccessfully == rateLimitDecorator.MaxRequests) {
context.Response.StatusCode = (int) HttpStatusCode.TooManyRequests;
return;
}
await UpdateClientStatisticsAsync(key, rateLimitDecorator.MaxRequests);
await _next(context);
}





Join the conversation! Your thoughts help the community grow.