Modern .NET includes native performance utilities that eliminate the need for third-party middleware packages: Rate Limiting protects your APIs from abuse and denial-of-service attempts, while output caching drastically reduces database load by caching full HTTP responses server-side.
Step 1: Configuring Built-In Rate Limiting
Rate limiting controls the frequency of requests clients can make to your API. .NET offers fixed window, sliding window, token bucket, and concurrency limiters.
Configure a fixed window rate limiter policy in Program.cs:
Code snippet
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: context.User.Identity?.Name ?? context.Request.Headers.Host.ToString(),
factory: _ => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 5, // Max 5 requests
QueueLimit = 2, // Queue up to 2 requests if limit is hit
Window = TimeSpan.FromSeconds(10) // Per 10-second window
}));
// Custom response when rate limit is exceeded
options.OnRejected = async (context, cancellationToken) =>
{
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
await context.HttpContext.Response.WriteAsync("Rate limit exceeded. Please try again later.", cancellationToken);
};
});
Step 2: Configuring Output Caching
Output caching stores the output of HTTP requests and serves them from memory for subsequent identical requests, bypassing controller logic and database queries entirely.
Add output caching services:
C#
builder.Services.AddOutputCache(options =>
{
// Define a default policy
options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromSeconds(30)));
// Define a custom named policy
options.AddPolicy("ExpireByMinute", builder => builder.Expire(TimeSpan.FromMinutes(1)));
});
builder.Services.AddControllers();
var app = builder.Build();
Step 3: Applying Rate Limiting and Output Caching to Endpoints
Combine output caching and rate limiting policies on your controllers or minimal endpoints using standard attributes or fluent extension methods.
C#
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.AspNetCore.RateLimiting;
[ApiController]
[Route("api/reports")]
public class ReportsController : ControllerBase
{
// Apply Output Caching with a specific policy
[HttpGet("summary")]
[OutputCache(PolicyName = "ExpireByMinute")]
public async Task<IActionResult> GetReportSummary()
{
// Simulate an expensive database operation
await Task.Delay(1000);
return Ok(new { GeneratedAt = DateTime.UtcNow, Data = "Expensive enterprise analytics dataset." });
}
// Apply specific Rate Limiting policy or disable it for public routes
[HttpGet("public-status")]
[EnableRateLimiting("PublicPolicy")]
public IActionResult GetStatus()
{
return Ok(new { Status = "System Online" });
}
}
Step 4: Complete Pipeline Integration in Program.cs
Ensure the middleware components are executed in the correct sequence within your application startup flow:
C#
var app = builder.Build();
// 1. Enable Rate Limiting middleware
app.UseRateLimiter();
app.UseHttpsRedirection();
app.UseAuthorization();
// 2. Enable Output Caching middleware
app.UseOutputCache();
app.MapControllers();
app.Run();
Join the conversation! Your thoughts help the community grow.