Ever run into a situation where one of your API consumers are being a bit too greedy, accessing your endpoints a bit too frequently, causing unduly strain on your resources? Today, we’ll look at how we can rate-limit our APIs so that all your consumers will have a pleasant experience using your API with no one user causing service degradation for others. As with all things .NET, you have a few different ways to solve this problem. In today’s post, we’ll look at how we can solve this problem by utilizing a popular library – AspNetCoreRateLimit.
What is Rate Limiting and Why Should I Implement It?
Rate Limiting is a mechanism by which you control the network access to your API or other resources by throttling the rate at which it is accessed. There are varied reasons why you may want to implement something like this:
- Defend against Distributed Denial of Service (DDoS) attacks or other abusive behavior.
- Provide different tiers of usage for your consumers, even pricing usage accordingly.
- Defend against run away scripts or processes.
There are many elegant options available to address this concern in your code and today we’ll look at AspNetCoreRateLimit, an open source project that has been around for quite some time.
Setup and Configure AspNetCoreRateLimit
You can install this library via NuGet.
dotnet add package AspNetCoreRateLimit
Next, let’s wire it up during startup in our Program.cs
(or Startup.cs
).
using AspNetCoreRateLimit; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Needed to store rate limit counters and ip rules builder.Services.AddMemoryCache(); // Load general configuration from appsettings.json builder.Services.Configure<IpRateLimitOptions>(builder.Configuration.GetSection("IpRateLimiting")); // Load ip rules from appsettings.json builder.Services.Configure<IpRateLimitPolicies>(builder.Configuration.GetSection("IpRateLimitPolicies")); // Inject counter and rules stores builder.Services.AddInMemoryRateLimiting(); // Configuration (resolvers, counter key builders) builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(); var app = builder.Build(); // Use it app.UseIpRateLimiting(); app.MapControllers(); app.Run();
Next, let’s define the rules of our rate limiting. One way to achieve this is by specifying them in our appsettings.json class (or via another IConfiguration source).
{ "IpRateLimiting": { "EnableEndpointRateLimiting": false, "StackBlockedRequests": false, "RealIpHeader": "X-Real-IP", "ClientIdHeader": "X-ClientId", "HttpStatusCode": 429, "GeneralRules": [ { "Endpoint": "*", "Period": "15s", "Limit": 2 }, { "Endpoint": "*", "Period": "15m", "Limit": 100 }, { "Endpoint": "*", "Period": "12h", "Limit": 1000 }, { "Endpoint": "*", "Period": "7d", "Limit": 10000 } ] }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
In the rules above, we are specifying that requests to any endpoint should be rate limited. No more than 2 requests should be allowed per every 15 seconds, 100 requests per 15 minutes, 1000 per 12 hours and 10,000 per 7 days. If the quotas are exceeded, the caller should be given a 429 HTTP Status Code – Too Many Requests.
Let’s run the app and try to test one of our API endpoints via Postman.
Above you can see that when I try to make three requests consecutively, the first two succeed with an HTTP Status Code of 200 (OK) and the data is returned. However, on the third try, the API returns a 429 (Too Many Requests) Status Code. The textual response indicates that I have exceeded the allowed quota.
Closing Remarks
Rate limiting your APIs provide an extra layer of protection in case of a runaway process or a more deliberate attack. It also allows you to provide different levels of service for your different consumers. AspNetCoreRateLimit simplifies the implementation of rate limiting in ASP.NET Core applications by offering a comprehensive set of features and configuration options. By leveraging this powerful library, you can effectively control the request rate, protect your resources, and provide a reliable experience to your clients.