Files
react-dotnet-framework/backend/Gateway/GatewayServiceCollectionExtensions.cs
2026-08-03 04:23:32 -04:00

65 lines
2.4 KiB
C#

using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;
namespace Backend.Gateway;
public static class GatewayServiceCollectionExtensions
{
public const string CorsPolicyName = "DefaultCorsPolicy";
public static IServiceCollection AddApiGateway(
this IServiceCollection services,
IConfiguration configuration)
{
services.Configure<CorsOptions>(configuration.GetSection(CorsOptions.SectionName));
services.Configure<RateLimitOptions>(configuration.GetSection(RateLimitOptions.SectionName));
var corsSettings = configuration.GetSection(CorsOptions.SectionName).Get<CorsOptions>()
?? new CorsOptions();
var rateLimitSettings = configuration.GetSection(RateLimitOptions.SectionName).Get<RateLimitOptions>()
?? new RateLimitOptions();
services.AddCors(options => options.AddPolicy(CorsPolicyName, policy =>
{
if (corsSettings.AllowedOrigins.Length == 0)
{
return;
}
policy.WithOrigins(corsSettings.AllowedOrigins)
.AllowAnyHeader()
.AllowAnyMethod();
}));
services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.GlobalLimiter = CreateGlobalLimiter(rateLimitSettings);
});
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
return services;
}
// Health checks are exempt so that infrastructure probes are never throttled.
private static PartitionedRateLimiter<HttpContext> CreateGlobalLimiter(RateLimitOptions settings) =>
PartitionedRateLimiter.Create<HttpContext, string>(context =>
{
if (context.Request.Path.StartsWithSegments(ApiRoutes.Health))
{
return RateLimitPartition.GetNoLimiter("health");
}
var partitionKey = context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
return RateLimitPartition.GetFixedWindowLimiter(partitionKey, _ => new FixedWindowRateLimiterOptions
{
PermitLimit = settings.PermitLimit,
Window = TimeSpan.FromSeconds(settings.WindowSeconds),
QueueLimit = settings.QueueLimit,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
});
});
}