using System.Threading.RateLimiting;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.RateLimiting;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Web.BackOffice.Controllers;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Umbraco.Docs.Samples;
public class ApiRateLimiterComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
// Register the rate limiting services
builder.Services.AddRateLimiter(rateLimiterOptions =>
{
// Default is 503 (Service Unavailable)
rateLimiterOptions.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
// Use GlobalLimiter for all requests
// rateLimiterOptions.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
// {
// return RateLimitPartition.GetFixedWindowLimiter(
// partitionKey: httpContext.Request.Headers.Host.ToString(),
// partition => new FixedWindowRateLimiterOptions
// {
// PermitLimit = 2,
// Window = TimeSpan.FromSeconds(10),
// AutoReplenishment = true
// });
// });
// Add specific rate limiting policies
rateLimiterOptions.AddFixedWindowLimiter(policyName: "fixed1", options =>
{
options.PermitLimit = 2;
options.Window = TimeSpan.FromSeconds(10);
options.AutoReplenishment = true;
});
rateLimiterOptions.AddFixedWindowLimiter(policyName: "fixed2", options =>
{
options.PermitLimit = 5;
options.Window = TimeSpan.FromSeconds(10);
options.AutoReplenishment = true;
});
});
builder.Services.Configure<UmbracoPipelineOptions>(options =>
{
options.AddFilter(new UmbracoPipelineFilter("UmbracoApiRateLimiter")
{
PostRouting = postRouting =>
{
// Applying EnableRateLimitingAttribute to specific endpoint
postRouting.Use(async (context, next) =>
{
// Limit specific controller(s)
var controllerName = ControllerExtensions.GetControllerName<AuthenticationController>();
var actionName = nameof(AuthenticationController.PostRequestPasswordReset);
var currentEndpoint = context.GetEndpoint();
var actionDescriptor = currentEndpoint?.Metadata.GetMetadata<ControllerActionDescriptor>();
// Apply rate limiting logic based on your conditions
if (currentEndpoint is not null &&
actionDescriptor is not null &&
actionDescriptor.ControllerName == controllerName &&
actionDescriptor.ActionName == actionName)
{
// Add EnableRateLimiting attribute to endpoint's metadata
var endpointMetadataCollection = new EndpointMetadataCollection(
new List<object>(currentEndpoint.Metadata)
{
new EnableRateLimitingAttribute("fixed1")
});
// Update endpoint
var updatedEndpoint = new Endpoint(
currentEndpoint.RequestDelegate,
endpointMetadataCollection,
currentEndpoint.DisplayName);
context.SetEndpoint(updatedEndpoint);
}
await next.Invoke();
});
// Apply the RateLimitingMiddleware
// Must be called after UseRouting()
postRouting.UseRateLimiter();
}
});
});
}
}