Implementing rate limit with custom middleware in .NET

Luiz Eugênio Barbieri
Lodgify Technology Blog
4 min readOct 11, 2023

In this article, I will briefly discuss Rate Limiting and how to implement it in .NET Core applications using middleware. At the end of the reading, it is expected that you understand the concept of Rate Limiting, as well as being able to apply it in your .NET Core project.

This article also resulted in a GitHub repository with the working implementation of a web application in .NET Core 6 with a Rate Limit on its endpoints.

What is Rate Limiting?

Rate Limiting is a technique that controls how often a client can request a server or API. In other words, it limits the number of requests (maximum requests) a client can make within a specific period (time window).

Rate Limiting is widely used to add a layer of security to your application, to prevent DDoS attacks, and also as a way to market your application as SaaS, selling request packages.

With the content covered, let’s move on to practice.

Prerequisites

To exemplify in practice how to apply Rate Limiting through a middleware, I will use an ASP.NET Core Web API project with the following settings as a basis:

  • Framework: .NET 6.0 (Long Term Support)
  • Authentication type: None
  • Configure for HTTPS: false
  • Enable Docker: false
  • Use controllers: true
  • Enable OpenAPI support: true
  • Do not use top-level statements: true

Assigning Rate Limit to an endpoint

We will use a custom attribute to indicate that an endpoint will have a Rate Limit.

The attribute has two properties:

  • TimeWindowInSeconds: represents the time window in seconds that we will use to control access.
  • MaxRequests: represents the maximum number of hits an endpoint can receive for a given time window.

For example, if we define a time window of 60 seconds, with a maximum of 100 requests, we will block access when the client reaches the hundredth request within one minute.

Implementing the Rate Limit middleware

The middleware has a single public subscription method, public async Task InvokeAsync(HttpContext context).

The HttpContext it receives contains, as the name suggests, the Http context of the request in progress. In other words, it has information such as the requested endpoint and the client’s IP address, among additional information related to the request.

The first action of the middleware is to check whether the endpoint being requested is decorated with the rate limit attribute. For this, I used an extension method to encapsulate the method.

Its operation is simple. Take the context endpoint and check whether it has the Rate Limit attribute metadata. If so, it responds with true and sends the metadata in an out parameter.

If the endpoint in question has the attribute, the middleware’s second action will be to get the customer’s consumption data. I used another extension method to encapsulate how the data is collected.

For this example, I used DistributedCache to store consumption data to make the article less complex. This data could be saved in cache services such as Redis or any other of your choice in a productive implementation. A susceptible point to be considered is that the response to this query must be swift since all requests with Rate Limiting will go through it.

Note that the cache key is taken directly from the context, and this method has been extended, as some others mentioned in this article.

Returning to the main topic, if there is already consumption data for this client, the middleware’s third action will be to evaluate whether the client has consumed all the requests, and if it has reached the limit, the request will be interrupted. A Too Many Requests response will be sent to the customer.

If the client has not reached the request limit, the middleware’s fourth action will be increasing the number of requests.

The rules mentioned in the previous paragraphs were encapsulated in the data consumption class, which you can see below:

It is essential to mention that there is more than one Rate Limiter Algorithm, and the one used for this article was Fixed Window because it is the simplest since the objective of this article is to explain the concept of Rate Limiting.

If you want to know more about the different Rate Limiter Algorithms, check out this excellent article.

Then, we must save the updated consumption data and continue the request.

I present the last extension method used in this article below to save consumption data.

Last, but not least, remember to add your middleware to your WebApplication like this app.UseMiddleware<RateLimitMiddleware>();, and your app will be able to rate limiting your application.

Conclusion

Rate Limiting is an essential concept to be learned and applied in applications that aim to be scalable and secure.

We observed that its basic implementation is simple and practical and should become even more straightforward with Microsoft’s announcement that Rate Limiting will be distributed natively in .NET starting with version 7.

And if you’ve made it this far, I thank you for the minutes of your time you dedicated to reading this, and I invite you to share it with your friends and acquaintances.

Good knowledge is shared knowledge

PS: This article is also available in Portuguese here.

Excited to be part of our global mission to empower hospitality businesses? Explore our career opportunities on our dedicated page!

--

--

Luiz Eugênio Barbieri
Lodgify Technology Blog

.NET developer, Master in Applied Computing and information technology enthusiast.