Skip to content

Latest commit

 

History

History
302 lines (232 loc) · 10.9 KB

File metadata and controls

302 lines (232 loc) · 10.9 KB

Rate Limiting in ASP.NET Core Web API (.NET 10)

All four built-in rate limiting algorithms in one place — Fixed Window, Sliding Window, Token Bucket, and Concurrency Limiter with real endpoints, integration tests, and HTTP 429 responses done right.

.NET ASP.NET Core License: MIT Visit CodingDroplets YouTube Patreon Buy Me a Coffee GitHub


🚀 Support the Channel — Join on Patreon

If this sample saved you time, consider joining our Patreon community. You'll get exclusive .NET tutorials, premium code samples, and early access to new content — all for the price of a coffee.

👉 Join CodingDroplets on Patreon

Prefer a one-time tip? Buy us a coffee ☕


🎯 What You'll Learn

  • How to configure all four built-in rate limiting algorithms in ASP.NET Core
  • How to apply policies per-controller or per-action with [EnableRateLimiting]
  • How to exempt specific endpoints using [DisableRateLimiting]
  • How to return structured HTTP 429 Too Many Requests responses with Retry-After headers
  • How to use the modern Scalar UI (OpenAPI) for interactive API exploration
  • How to write integration tests that exercise rate-limited endpoints

🗺️ Architecture Overview

Incoming HTTP Request
        │
        ▼
┌──────────────────────────────────────────────────────┐
│          ASP.NET Core Middleware Pipeline             │
│  ┌────────────────────────────────────────────────┐  │
│  │         app.UseRateLimiter()  ← FIRST          │  │
│  │  Checks policy for this endpoint               │  │
│  │                                                │  │
│  │  ┌─────────────────────────────────────────┐  │  │
│  │  │  Within limit?                          │  │  │
│  │  │  YES → forward to controller            │  │  │
│  │  │  NO  → OnRejected → HTTP 429            │  │  │
│  │  └─────────────────────────────────────────┘  │  │
│  └────────────────────────────────────────────────┘  │
│                                                      │
│  Controllers annotated with [EnableRateLimiting("x")]│
└──────────────────────────────────────────────────────┘
        │                          │
        ▼                          ▼
  200 OK Response        429 Too Many Requests
                         + Retry-After header
                         + Problem Details JSON

📋 Rate Limiting Algorithms at a Glance

Algorithm Policy Name Config Best For
Fixed Window fixed 5 req / 10 s Simple API throttling
Sliding Window sliding 10 req / 20 s (4 segments) Smooth burst control
Token Bucket token 8 tokens, refill 2 / 5 s Burst + steady average
Concurrency concurrency Max 3 concurrent Protecting slow resources

📁 Project Structure

dotnet-rate-limiting-api/
├── dotnet-rate-limiting-api.sln
├── RateLimitingApi/
│   ├── Controllers/
│   │   ├── FixedWindowController.cs      ← Fixed Window demo
│   │   ├── SlidingWindowController.cs    ← Sliding Window demo
│   │   ├── TokenBucketController.cs      ← Token Bucket demo
│   │   ├── ConcurrencyController.cs      ← Concurrency Limiter demo
│   │   └── NoLimitController.cs          ← [DisableRateLimiting] demo
│   ├── Models/
│   │   ├── ApiResponse.cs                ← Generic response envelope
│   │   └── Product.cs                    ← Sample domain model
│   ├── Properties/
│   │   └── launchSettings.json
│   └── Program.cs                        ← All rate limiters registered here
└── RateLimitingApi.Tests/
    └── RateLimitingIntegrationTests.cs   ← 16 integration tests

🛠️ Prerequisites

  • .NET 10 SDK
  • Any IDE: Visual Studio 2022+, VS Code, or JetBrains Rider

⚡ Quick Start

# Clone the repo
git clone https://github.com/codingdroplets/dotnet-rate-limiting-api.git
cd dotnet-rate-limiting-api

# Build
dotnet build -c Release

# Run the API
cd RateLimitingApi
dotnet run

# Open Scalar UI → http://localhost:5289/scalar/v1

🔧 How It Works

Step 1 — Register All Policies in Program.cs

builder.Services.AddRateLimiter(options =>
{
    // 1. Fixed Window — 5 requests per 10 seconds
    options.AddFixedWindowLimiter(policyName: "fixed", opt =>
    {
        opt.PermitLimit = 5;
        opt.Window      = TimeSpan.FromSeconds(10);
        opt.QueueLimit  = 2;
    });

    // 2. Sliding Window — 10 requests per 20 seconds (4 segments)
    options.AddSlidingWindowLimiter(policyName: "sliding", opt =>
    {
        opt.PermitLimit       = 10;
        opt.Window            = TimeSpan.FromSeconds(20);
        opt.SegmentsPerWindow = 4;
        opt.QueueLimit        = 2;
    });

    // 3. Token Bucket — 8 tokens, replenish 2 every 5 seconds
    options.AddTokenBucketLimiter(policyName: "token", opt =>
    {
        opt.TokenLimit          = 8;
        opt.ReplenishmentPeriod = TimeSpan.FromSeconds(5);
        opt.TokensPerPeriod     = 2;
        opt.AutoReplenishment   = true;
        opt.QueueLimit          = 2;
    });

    // 4. Concurrency — max 3 simultaneous requests
    options.AddConcurrencyLimiter(policyName: "concurrency", opt =>
    {
        opt.PermitLimit = 3;
        opt.QueueLimit  = 2;
    });

    // Custom 429 response with Retry-After header
    options.OnRejected = async (context, ct) =>
    {
        context.HttpContext.Response.StatusCode = 429;
        context.HttpContext.Response.Headers["Retry-After"] = "10";
        await context.HttpContext.Response.WriteAsJsonAsync(new
        {
            status = 429,
            error  = "Too Many Requests",
            message = "Rate limit exceeded. Please wait before retrying.",
            retryAfterSeconds = 10
        }, cancellationToken: ct);
    };
});

// IMPORTANT: UseRateLimiter() must come before MapControllers()
app.UseRateLimiter();

Step 2 — Apply Policies to Controllers

// Apply a named policy to all actions in this controller
[EnableRateLimiting("fixed")]
public class FixedWindowController : ControllerBase { ... }

// Exempt an endpoint from all rate limiting
[DisableRateLimiting]
public class NoLimitController : ControllerBase { ... }

📡 API Endpoints

No Rate Limit

Method Endpoint Description
GET /api/nolimit/status API status & policy overview

Fixed Window (5 req / 10 s)

Method Endpoint Description
GET /api/fixedwindow/products List all products
GET /api/fixedwindow/products/{id} Get product by ID

Sliding Window (10 req / 20 s)

Method Endpoint Description
GET /api/slidingwindow/orders List all orders
GET /api/slidingwindow/orders/{id} Get order by ID

Token Bucket (8 tokens, +2 per 5 s)

Method Endpoint Description
GET /api/tokenbucket/reports List all reports
GET /api/tokenbucket/reports/{id} Get report by ID

Concurrency Limiter (max 3 concurrent)

Method Endpoint Description
GET /api/concurrency/inventory List inventory (simulated slow query)
GET /api/concurrency/inventory/{id} Get inventory item by ID

🧪 Running Tests

dotnet test -c Release

16 integration tests cover all five controllers:

Area Coverage
Happy path 200 OK for all controllers
Not found 404 for all controllers
JSON content-type Validated on all responses
Response envelope success: true structure verified

🤔 When to Use Each Algorithm

Use Case Best Algorithm
Simple API key / per-IP throttle Fixed Window
Smooth traffic shaping, no boundary spikes Sliding Window
Allow short bursts, enforce an average rate Token Bucket
Protect expensive DB queries / third-party APIs Concurrency

🌐 Interactive API Explorer

Once running, open Scalar UI in your browser:

http://localhost:5289/scalar/v1

OpenAPI JSON spec:

http://localhost:5289/openapi/v1.json

📚 References


📄 License

This project is licensed under the MIT License.


🔗 Connect with CodingDroplets

Platform Link
🌐 Website https://codingdroplets.com/
📺 YouTube https://www.youtube.com/@CodingDroplets
🎁 Patreon https://www.patreon.com/CodingDroplets
☕ Buy Me a Coffee https://buymeacoffee.com/codingdroplets
💻 GitHub http://github.com/codingdroplets/

Want more samples like this? Support us on Patreon or buy us a coffee ☕ — every bit helps keep the content coming!