How to Implementing Rate-Limiting Middleware in .NET Core 8

Rate-limiting is an essential technique to protect your APIs from abuse, such as overuse or Distributed Denial of Service (DDoS) attacks. By controlling the number of requests a user can make within a given time frame, you can ensure fair usage and prevent system overload. In this tutorial, we will create a rate-limiting middleware using .NET Core 8 in Visual Studio 2022. The middleware will identify users by their IP addresses or API keys, count their requests, enforce limits, and return a 429 Too Many Requests response when limits are exceeded.

By the end of this article, you will have a fully functional project demonstrating how to implement rate-limiting middleware in a real-life scenario.

Before starting, ensure you have the following:

  • Visual Studio 2022 installed
  • .NET Core 8 SDK installed
  • Basic understanding of ASP.NET Core middleware

Setting Up the Project

1. Create a New Project:

  • Open Visual Studio 2022 and select Create a new project.
  • Choose ASP.NET Core Web API and click Next.
  • Name the project RateLimitingMiddlewareDemo and select the location.
  • Click Next, select .NET 8, and click Create.

2. Install Required Packages:

  • Open the Package Manager Console and run the following command to add the required NuGet package for memory caching:
Install-Package Microsoft.Extensions.Caching.Memory

This package provides an in-memory cache implementation that allows you to temporarily store and retrieve data efficiently within your application.

Creating the Project File Structure

Organize the project with the following structure:

Implementing Core Features

1. Create the RateLimitRule Model

Create a RateLimitRule model in the Models folder to define rate-limiting rules:

namespace RateLimitingMiddlewareDemo.Models
{
    public class RateLimitRule
    {
        public int Limit { get; set; }
        public TimeSpan Window { get; set; }
    }
}

2. Create the RateLimitingMiddleware

Add a new RateLimitingMiddleware class in the Middleware folder:

using Microsoft.Extensions.Caching.Memory;
using Microsoft.AspNetCore.Http;
using RateLimitingMiddlewareDemo.Models;
using System.Net;
using System.Text.Json;

namespace RateLimitingMiddlewareDemo.Middleware
{
    public class RateLimitingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IMemoryCache _cache;
        private readonly RateLimitRule _rule;

        public RateLimitingMiddleware(RequestDelegate next, IMemoryCache cache, RateLimitRule rule)
        {
            _next = next;
            _cache = cache;
            _rule = rule;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var identifier = context.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
            var cacheKey = $"RateLimit_{identifier}";

            if (!_cache.TryGetValue(cacheKey, out int requestCount))
            {
                requestCount = 0;
                _cache.Set(cacheKey, requestCount, _rule.Window);
            }

            if (requestCount >= _rule.Limit)
            {
                context.Response.StatusCode = (int)HttpStatusCode.TooManyRequests;
                context.Response.ContentType = "application/json";

                var response = new { Message = "Rate limit exceeded. Try again later." };
                await context.Response.WriteAsync(JsonSerializer.Serialize(response));
                return;
            }

            _cache.Set(cacheKey, ++requestCount, _rule.Window);
            await _next(context);
        }
    }
}

3. Register the Middleware in Program.cs

Update Program.cs to configure the middleware:

using Microsoft.Extensions.Caching.Memory;
using RateLimitingMiddlewareDemo.Middleware;
using RateLimitingMiddlewareDemo.Models;

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();

// Add memory cache service for rate limiting
builder.Services.AddMemoryCache();


var app = builder.Build();

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



var rateLimitRule = new RateLimitRule
{
    Limit = 5,
    Window = TimeSpan.FromSeconds(10)
};

app.UseMiddleware<RateLimitingMiddleware>(app.Services.GetRequiredService<IMemoryCache>(), rateLimitRule);

app.MapControllers();

//app.MapGet("/api/sample", () => "Hello, World!");

app.Run();

4. Add a Sample Controller

Create a SampleController in the Controllers folder to demonstrate the middleware:

using Microsoft.AspNetCore.Mvc;

namespace RateLimitingMiddlewareDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class SampleController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Request successful!");
        }
    }
}

Testing the Implementation

1. Run the application by pressing F5 in Visual Studio.

2. Use a tool like Postman or cURL to send requests to the /api/sample endpoint.

  • Example using cURL:
curl https://localhost:7183/api/Sample

3. Observe the responses:

  • For the first 5 requests within 10 seconds, you will receive a 200 OK response.
  • After the 5th request, a 429 Too Many Requests response will be returned.

Download Source Code

To download the free source code from this tutorial, click the button below.

Important Notes:

  • Ensure you have 7-Zip installed to extract the file. You can download 7-Zip here if you don’t already have it.
  • Use the password freecodespot when prompted during extraction.

This source code is designed to work seamlessly with the steps outlined in this tutorial. If you encounter any issues or have questions, feel free to reach out in the comments section below.

Summary

In this tutorial, we implemented rate-limiting middleware in .NET Core 8 to protect an API from abuse and overuse. The middleware identifies users by IP address, counts requests, enforces rate limits, and returns a 429 Too Many Requests response when limits are exceeded. This technique is essential for safeguarding your APIs from DDoS attacks and ensuring fair usage among clients.