diff --git a/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingContext.cs b/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingContext.cs
index 3f408b8240..f75c06b05b 100644
--- a/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingContext.cs
+++ b/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingContext.cs
@@ -8,8 +8,9 @@ public class OperationRateLimitingContext
{
///
/// Optional parameter passed by the caller.
- /// Used by rules configured with PartitionByParameter().
- /// Can be email, phone number, resource id, or any string.
+ /// Used as the partition key by PartitionByParameter() (required),
+ /// and as a fallback by PartitionByEmail() and PartitionByPhoneNumber().
+ /// Can be email, phone number, user id, resource id, or any string.
///
public string? Parameter { get; set; }
diff --git a/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleBuilder.cs b/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleBuilder.cs
index 82ed356f01..b9f2eacf9a 100644
--- a/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleBuilder.cs
+++ b/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleBuilder.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimiting;
@@ -8,7 +9,7 @@ public class OperationRateLimitingRuleBuilder
private TimeSpan _duration;
private int _maxCount;
private OperationRateLimitingPartitionType? _partitionType;
- private Func? _customPartitionKeyResolver;
+ private Func>? _customPartitionKeyResolver;
private bool _isMultiTenant;
internal bool IsCommitted { get; private set; }
@@ -43,7 +44,8 @@ public class OperationRateLimitingRuleBuilder
}
///
- /// Auto resolve from ICurrentUser.Id.
+ /// Partition by the current authenticated user (ICurrentUser.Id).
+ /// Use PartitionByParameter() if you need to specify the user ID explicitly.
///
public OperationRateLimitingPolicyBuilder PartitionByCurrentUser()
{
@@ -53,7 +55,7 @@ public class OperationRateLimitingRuleBuilder
}
///
- /// Auto resolve from ICurrentTenant.Id.
+ /// Partition by the current tenant (ICurrentTenant.Id). Uses "host" when no tenant is active.
///
public OperationRateLimitingPolicyBuilder PartitionByCurrentTenant()
{
@@ -63,7 +65,8 @@ public class OperationRateLimitingRuleBuilder
}
///
- /// Auto resolve from IWebClientInfoProvider.ClientIpAddress.
+ /// Partition by the client IP address (IWebClientInfoProvider.ClientIpAddress).
+ /// Use PartitionByParameter() if you need to specify the IP explicitly.
///
public OperationRateLimitingPolicyBuilder PartitionByClientIp()
{
@@ -95,10 +98,10 @@ public class OperationRateLimitingRuleBuilder
}
///
- /// Custom partition key resolver from context.
+ /// Custom async partition key resolver from context.
///
public OperationRateLimitingPolicyBuilder PartitionBy(
- Func keyResolver)
+ Func> keyResolver)
{
_partitionType = OperationRateLimitingPartitionType.Custom;
_customPartitionKeyResolver = Check.NotNull(keyResolver, nameof(keyResolver));
diff --git a/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleDefinition.cs b/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleDefinition.cs
index a3415105d9..f8d1bcf9e7 100644
--- a/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleDefinition.cs
+++ b/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleDefinition.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimiting;
@@ -10,7 +11,7 @@ public class OperationRateLimitingRuleDefinition
public OperationRateLimitingPartitionType PartitionType { get; set; }
- public Func? CustomPartitionKeyResolver { get; set; }
+ public Func>? CustomPartitionKeyResolver { get; set; }
public bool IsMultiTenant { get; set; }
}
diff --git a/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Rules/FixedWindowOperationRateLimitingRule.cs b/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Rules/FixedWindowOperationRateLimitingRule.cs
index af072bc8c4..7df79a0052 100644
--- a/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Rules/FixedWindowOperationRateLimitingRule.cs
+++ b/framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Rules/FixedWindowOperationRateLimitingRule.cs
@@ -38,7 +38,7 @@ public class FixedWindowOperationRateLimitingRule : IOperationRateLimitingRule
public virtual async Task AcquireAsync(
OperationRateLimitingContext context)
{
- var partitionKey = ResolvePartitionKey(context);
+ var partitionKey = await ResolvePartitionKeyAsync(context);
var storeKey = BuildStoreKey(partitionKey);
var storeResult = await Store.IncrementAsync(storeKey, Definition.Duration, Definition.MaxCount);
@@ -48,7 +48,7 @@ public class FixedWindowOperationRateLimitingRule : IOperationRateLimitingRule
public virtual async Task CheckAsync(
OperationRateLimitingContext context)
{
- var partitionKey = ResolvePartitionKey(context);
+ var partitionKey = await ResolvePartitionKeyAsync(context);
var storeKey = BuildStoreKey(partitionKey);
var storeResult = await Store.GetAsync(storeKey, Definition.Duration, Definition.MaxCount);
@@ -57,12 +57,12 @@ public class FixedWindowOperationRateLimitingRule : IOperationRateLimitingRule
public virtual async Task ResetAsync(OperationRateLimitingContext context)
{
- var partitionKey = ResolvePartitionKey(context);
+ var partitionKey = await ResolvePartitionKeyAsync(context);
var storeKey = BuildStoreKey(partitionKey);
await Store.ResetAsync(storeKey);
}
- protected virtual string ResolvePartitionKey(OperationRateLimitingContext context)
+ protected virtual async Task ResolvePartitionKeyAsync(OperationRateLimitingContext context)
{
return Definition.PartitionType switch
{
@@ -71,17 +71,20 @@ public class FixedWindowOperationRateLimitingRule : IOperationRateLimitingRule
$"OperationRateLimitingContext.Parameter is required for policy '{PolicyName}' (PartitionByParameter)."),
OperationRateLimitingPartitionType.CurrentUser =>
- CurrentUser.Id?.ToString() ?? throw new AbpException(
- $"Current user is not authenticated. Policy '{PolicyName}' requires PartitionByCurrentUser."),
+ CurrentUser.Id?.ToString()
+ ?? throw new AbpException(
+ $"Current user is not authenticated. Policy '{PolicyName}' requires PartitionByCurrentUser. " +
+ "Use PartitionByParameter() if you need to specify the user ID explicitly."),
OperationRateLimitingPartitionType.CurrentTenant =>
- CurrentTenant.Id?.ToString() ?? HostTenantKey,
+ CurrentTenant.Id?.ToString()
+ ?? HostTenantKey,
OperationRateLimitingPartitionType.ClientIp =>
WebClientInfoProvider.ClientIpAddress
?? throw new AbpException(
$"Client IP address could not be determined. Policy '{PolicyName}' requires PartitionByClientIp. " +
- "Ensure IWebClientInfoProvider is properly configured."),
+ "Ensure IWebClientInfoProvider is properly configured or use PartitionByParameter() to pass the IP explicitly."),
OperationRateLimitingPartitionType.Email =>
context.Parameter
@@ -96,7 +99,7 @@ public class FixedWindowOperationRateLimitingRule : IOperationRateLimitingRule
$"Phone number is required for policy '{PolicyName}' (PartitionByPhoneNumber). Provide it via context.Parameter or ensure the user has a phone number."),
OperationRateLimitingPartitionType.Custom =>
- Definition.CustomPartitionKeyResolver!(context),
+ await Definition.CustomPartitionKeyResolver!(context),
_ => throw new AbpException($"Unknown partition type: {Definition.PartitionType}")
};
diff --git a/framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingTestModule.cs b/framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingTestModule.cs
index 6bee2b7d83..7a43716927 100644
--- a/framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingTestModule.cs
+++ b/framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingTestModule.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Volo.Abp.AspNetCore.WebClientInfo;
@@ -157,7 +158,7 @@ public class AbpOperationRateLimitingTestModule : AbpModule
options.AddPolicy("TestCustomResolver", policy =>
{
policy.WithFixedWindow(TimeSpan.FromHours(1), maxCount: 2)
- .PartitionBy(ctx => $"action:{ctx.Parameter}");
+ .PartitionBy(ctx => Task.FromResult($"action:{ctx.Parameter}"));
});
// Multi-tenant: ByParameter with tenant isolation - same param, different tenants = different counters
diff --git a/framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingPolicyBuilder_Tests.cs b/framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingPolicyBuilder_Tests.cs
index 56347cf53b..6a503a6191 100644
--- a/framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingPolicyBuilder_Tests.cs
+++ b/framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingPolicyBuilder_Tests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using Shouldly;
using Xunit;
@@ -74,7 +75,7 @@ public class OperationRateLimitingPolicyBuilder_Tests
{
policy.AddRule(rule => rule
.WithFixedWindow(TimeSpan.FromMinutes(30), maxCount: 5)
- .PartitionBy(ctx => $"custom:{ctx.Parameter}"));
+ .PartitionBy(ctx => Task.FromResult($"custom:{ctx.Parameter}")));
});
var policy = options.Policies["CustomPolicy"];
@@ -242,11 +243,11 @@ public class OperationRateLimitingPolicyBuilder_Tests
{
policy.AddRule(rule => rule
.WithFixedWindow(TimeSpan.FromHours(1), maxCount: 5)
- .PartitionBy(ctx => $"by-ip:{ctx.Parameter}"));
+ .PartitionBy(ctx => Task.FromResult($"by-ip:{ctx.Parameter}")));
policy.AddRule(rule => rule
.WithFixedWindow(TimeSpan.FromHours(1), maxCount: 5)
- .PartitionBy(ctx => $"by-device:{ctx.Parameter}"));
+ .PartitionBy(ctx => Task.FromResult($"by-device:{ctx.Parameter}")));
});
var policy = options.Policies["MultiCustomPolicy"];