محدود سازی نرخ دسترسی به منابع در برنامههای ASP.NET Core - قسمت دوم - پیاده سازی
نویسنده: وحید نصیری
تاریخ: ۱۴۰۱/۱۱/۲۹ ۸:۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.User.Identity?.Name ?? httpContext.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 10,
QueueLimit = 0,
Window = TimeSpan.FromMinutes(1)
}));
});
// ...
var app = builder.Build();
// ...
app.UseRouting();
app.UseRateLimiter();
app.MapGet("/", () => "Hello World!");
app.Run(); builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = 429;
// ...
}); builder.Services.AddRateLimiter(options =>
{
options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;
if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
await context.HttpContext.Response.WriteAsync(
$"Too many requests. Please try again after {retryAfter.TotalMinutes} minute(s). " +
$"Read more about our rate limits at https://example.org/docs/ratelimiting.", cancellationToken: token);
}
else
{
await context.HttpContext.Response.WriteAsync(
"Too many requests. Please try again later. " +
"Read more about our rate limits at https://example.org/docs/ratelimiting.", cancellationToken: token);
}
};
// ...
}); context.HttpContext.RequestServices.GetService<ILoggerFactory>()?
.CreateLogger("Microsoft.AspNetCore.RateLimitingMiddleware")
.LogWarning("OnRejected: {RequestPath}", context.HttpContext.Request.Path); builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.CreateChained(
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(httpContext.ResolveClientIpAddress(), partition =>
new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 600,
Window = TimeSpan.FromMinutes(1)
})),
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(httpContext.ResolveClientIpAddress(), partition =>
new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 6000,
Window = TimeSpan.FromHours(1)
})));
// ...
}); PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(httpContext.ResolveClientIpAddress(), partition =>
new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 10,
QueueLimit = 6,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
Window = TimeSpan.FromSeconds(1)
}))); public class ExampleRateLimiterPolicy : IRateLimiterPolicy<string>
{
public RateLimitPartition<string> GetPartition(HttpContext httpContext)
{
if (httpContext.User.Identity?.IsAuthenticated == true)
{
return RateLimitPartition.GetFixedWindowLimiter(httpContext.User.Identity.Name!,
partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 1_000,
Window = TimeSpan.FromMinutes(1),
});
}
return RateLimitPartition.GetFixedWindowLimiter(httpContext.Request.Headers.Host.ToString(),
partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1),
});
}
public Func<OnRejectedContext, CancellationToken, ValueTask>? OnRejected { get; } =
(context, _) =>
{
context.HttpContext.Response.StatusCode = 418; // I'm a 🫖
return new ValueTask();
};
} options.AddPolicy<string, ExampleRateLimiterPolicy>("myPolicy"); builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("Api", options =>
{
options.AutoReplenishment = true;
options.PermitLimit = 10;
options.Window = TimeSpan.FromMinutes(1);
});
options.AddFixedWindowLimiter("Web", options =>
{
options.AutoReplenishment = true;
options.PermitLimit = 10;
options.Window = TimeSpan.FromMinutes(1);
});
// ...
}); options.AddPolicy("Api", httpContext =>
RateLimitPartition.GetFixedWindowLimiter(httpContext.ResolveClientIpAddress(),
partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 10,
Window = TimeSpan.FromSeconds(1)
})); // Endpoint
app.MapGet("/api/hello", () => "Hello World!").RequireRateLimiting("Api");
// Group
app.MapGroup("/api/orders").RequireRateLimiting("Api"); // Endpoint
app.MapGet("/api/hello", () => "Hello World!").DisableRateLimiting();
// Group
app.MapGroup("/api/orders").DisableRateLimiting(); [EnableRateLimiting("Api")]
public class Orders : Controller
{
[DisableRateLimiting]
public IActionResult Index()
{
return View();
}
[EnableRateLimiting("ApiListing")]
public IActionResult List()
{
return View();
}
} app.UseConfiguredEndpoints(endpoints =>
{
endpoints.MapRazorPages()
.DisableRateLimiting();
endpoints.MapControllers()
.RequireRateLimiting("UserBasedRateLimiting");
}); Set up a Redis backplane for ASP.NET Core multi-node deployments, using the built-in Rate Limiting support that's part of .NET 7.
app.UseRouting(); app.UseRateLimiter();