Menu Close

Rate Limiting in ASP.Net Core Web API using middleware

In this article we are going to discuss about Rate Limiting in ASP.Net Core Web API using middleware. 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. Please read my previous article Implement Rate Limiting in ASP.Net Core Web API, where it discussed how we can use rate limiting using Nuget Packages. Here we discuss how we can apply rate limiting using Middleware instead of use Nuget package.

When developing APIs, regardless of the framework or language used, you may want to limit the number of requests an API accepts within a given time period. This is accomplished through the use of a rate-limiting strategy, which helps to limit network traffic. In this article, I’ll show you how to create a simple rate-limiting middleware in ASP.NET Core.

Find Source Code

What steps must be taken to achieve Middleware Rate Limiting?

RateLimitMiddlware

This Middleware aborts an API call if a client sends more requests than the API can handle in the time frame we specified for the executing API by decorating them with a class (i.e., RateLimitDecorator) that describes a rate-limiting strategy.

LimitRequests

This class to choose a strategy needed for request throttling.

Implement Rate Limiting in the ASP.NET Core

Let’s create an ASP.NET Core project in Visual Studio. Follow the steps outlined below to create a new ASP.NET Core project in Visual Studio.

  • Click on “Create new project” and select “ASP.NET Core Web API Application” from the list of templates displayed
  • Choose .NET 6.0 and click Next.
  • You can make check boxes enabled for “Enable Docker Support” and “Configure for HTTPS” if you want to use it further else you can make unchecked it.
  • Ensure that Authentication is set as “No Authentication” as we won’t be using authentication either.

Adding the middleware class to implement Rate Limit

Right Click on project and add the middleware class like below and modify the code accordingly.

rate-limiting-middleware
  • The highlighted lines here indicate that if multiple calls occur within 5 seconds, the status code 429 will be displayed.
namespace RateLimiting_Middleware_NetCoreAPI.Middleware
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
    public class RateLimitMiddleware
    {
        private readonly RequestDelegate _next;
        static readonly ConcurrentDictionary<string, DateTime?> ApiCallsInMemory = new();
        public RateLimitMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var endpoint = context.GetEndpoint();
            var controllerActionDescriptor = endpoint?.Metadata.GetMetadata<ControllerActionDescriptor>();

            if (controllerActionDescriptor is null)
            {
                await _next(context);
                return;
            }

            var apiDecorator = (LimitRequests)controllerActionDescriptor.MethodInfo
                            .GetCustomAttributes(true)
                            .SingleOrDefault(w => w.GetType() == typeof(LimitRequests));

            if (apiDecorator is null)
            {
                await _next(context);
                return;
            }

            string key = GetCurrentClientKey(apiDecorator, context);

            var previousApiCall = GetPreviousApiCallByKey(key);
            if (previousApiCall != null)
            {

                if (DateTime.Now < previousApiCall.Value.AddSeconds(5))
                {
                    context.Response.StatusCode = (int)HttpStatusCode.TooManyRequests;
                    return;
                }
            }

            UpdateApiCallFor(key);

            await _next(context);
        }

        /// <summary>
        /// We store the time that a client made a call to the current API
        /// </summary>
        /// <param name="key"></param>
        private void UpdateApiCallFor(string key)
        {
            ApiCallsInMemory.TryRemove(key, out _);
            ApiCallsInMemory.TryAdd(key, DateTime.Now);
        }

        private DateTime? GetPreviousApiCallByKey(string key)
        {
            ApiCallsInMemory.TryGetValue(key, out DateTime? value);
            return value;
        }

        /// <summary>
        /// Makes key based on the specified strategy for the current API
        /// </summary>
        /// <param name="apiDecorator"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private static string GetCurrentClientKey(LimitRequests apiDecorator, HttpContext context)
        {
            var keys = new List<string>
            {
                context.Request.Path
            };

            if (apiDecorator.ConfigType == ConfigEnum.IpAddress)
                keys.Add(GetClientIpAddress(context));

            // TODO: Implement other strategies.

            return string.Join('_', keys);
        }

        /// <summary>
        /// Returns the client's Ip Address
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private static string GetClientIpAddress(HttpContext context)
        {
            return context.Connection.RemoteIpAddress.ToString();
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class RateLimitMiddlewareExtensions
    {
        public static IApplicationBuilder UseRateLimitMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RateLimitMiddleware>();
        }
    }
}

LimitRequests Type

It holds the enum values that can be shared our custom middleware to share the info. that which IPAdrees the request are coming etc.

   [AttributeUsage(AttributeTargets.Method)]
    public class LimitRequests : Attribute
    {
        public ConfigEnum ConfigType { get; set; }
    }
    public enum ConfigEnum
    {
        IpAddress,
        PerUser,
        PerApiKey
    }

Adding the custom middleware that we created in Program.cs

  • The highlighted middleware are added into the pipeline to enable rate limiting.
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

//Rate Limiting Middleware Added here
app.UseMiddleware<RateLimitMiddleware>();

app.MapControllers();

app.Run();

Finally, it is time to decorate our API with LimitRequestsDecorator to limit the number of requests it receives.

  • The highlighted line of filter, we added to mange the rate limiting the request for this controller.
 [HttpGet(Name = "GetWeatherForecast")]
 [LimitRequests( ConfigType = ConfigEnum.IpAddress)]
 public IEnumerable<WeatherForecast> Get()
    {
     return Enumerable.Range(1, 5).Select(index => new WeatherForecast
       {
          Date = DateTime.Now.AeddDays(index),
          TemperatureC = Random.Shared.Next(-20, 55),
          Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        }).ToArray();
     }

That’s it the changes from coding side,

Let’s run the application and access the controller in many times we see the result.

rate-limiting-middleware-output

Find Source Code

Conclusion

Here we discuss about Rate Limiting in ASP.Net Core Web API using middleware. 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. 

Leave behind your valuable queries and suggestions in the comment section below. Also, if you think this article helps you, do not forget to share this with your developer community. Happy Coding 🙂

Related Articles

Jayant Tripathy
Coder, Blogger, YouTuber

A passionate developer keep focus on learning and working on new technology.

Leave a Reply

Your email address will not be published.