diff --git a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs
index 374441474d..591545f48d 100644
--- a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs
+++ b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs
@@ -19,6 +19,8 @@ public interface IJsonApiOptions : ILinksConfiguration, ISerializerOptions
///
bool IncludeTotalRecordCount { get; set; }
int DefaultPageSize { get; }
+ int? MaximumPageSize { get; }
+ int? MaximumPageNumber { get; }
bool ValidateModelState { get; }
bool AllowClientGeneratedIds { get; }
bool AllowCustomQueryParameters { get; set; }
diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
index 0952a7fb0f..88e281fd5f 100644
--- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
+++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
@@ -70,6 +70,22 @@ public class JsonApiOptions : IJsonApiOptions
///
public int DefaultPageSize { get; set; }
+ ///
+ /// Optional. When set, limits the maximum page size for all resources.
+ ///
+ ///
+ /// options.MaximumPageSize = 50;
+ ///
+ public int? MaximumPageSize { get; set; }
+
+ ///
+ /// Optional. When set, limits the maximum page number for all resources.
+ ///
+ ///
+ /// options.MaximumPageNumber = 100;
+ ///
+ public int? MaximumPageNumber { get; set; }
+
///
/// Whether or not the total-record count should be included in all document
/// level meta objects.
diff --git a/src/JsonApiDotNetCore/QueryParameterServices/PageService.cs b/src/JsonApiDotNetCore/QueryParameterServices/PageService.cs
index 16883f1909..95179d85b0 100644
--- a/src/JsonApiDotNetCore/QueryParameterServices/PageService.cs
+++ b/src/JsonApiDotNetCore/QueryParameterServices/PageService.cs
@@ -83,6 +83,10 @@ public virtual void Parse(KeyValuePair queryParameter)
{
ThrowBadPagingRequest(queryParameter, "value needs to be greater than zero");
}
+ else if (size > _options.MaximumPageSize)
+ {
+ ThrowBadPagingRequest(queryParameter, $"page size cannot be higher than {_options.MaximumPageSize}.");
+ }
else
{
RequestedPageSize = size;
@@ -98,6 +102,10 @@ public virtual void Parse(KeyValuePair queryParameter)
{
ThrowBadPagingRequest(queryParameter, "page index is not zero-based");
}
+ else if (number > _options.MaximumPageNumber)
+ {
+ ThrowBadPagingRequest(queryParameter, $"page index cannot be higher than {_options.MaximumPageNumber}.");
+ }
else
{
Backwards = (number < 0);
diff --git a/test/UnitTests/QueryParameters/PageServiceTests.cs b/test/UnitTests/QueryParameters/PageServiceTests.cs
index eafafbcec3..c59b8a6e82 100644
--- a/test/UnitTests/QueryParameters/PageServiceTests.cs
+++ b/test/UnitTests/QueryParameters/PageServiceTests.cs
@@ -9,9 +9,13 @@ namespace UnitTests.QueryParameters
{
public class PageServiceTests : QueryParametersUnitTestCollection
{
- public PageService GetService()
+ public PageService GetService(int? maximumPageSize = null, int? maximumPageNumber = null)
{
- return new PageService(new JsonApiOptions());
+ return new PageService(new JsonApiOptions
+ {
+ MaximumPageSize = maximumPageSize,
+ MaximumPageNumber = maximumPageNumber
+ });
}
[Fact]
@@ -28,14 +32,16 @@ public void Name_PageService_IsCorrect()
}
[Theory]
- [InlineData("1", 1, false)]
- [InlineData("abcde", 0, true)]
- [InlineData("", 0, true)]
- public void Parse_PageSize_CanParse(string value, int expectedValue, bool shouldThrow)
+ [InlineData("1", 1, null, false)]
+ [InlineData("abcde", 0, null, true)]
+ [InlineData("", 0, null, true)]
+ [InlineData("5", 5, 10, false)]
+ [InlineData("5", 5, 3, true)]
+ public void Parse_PageSize_CanParse(string value, int expectedValue, int? maximumPageSize, bool shouldThrow)
{
// Arrange
var query = new KeyValuePair($"page[size]", new StringValues(value));
- var service = GetService();
+ var service = GetService(maximumPageSize: maximumPageSize);
// Act
if (shouldThrow)
@@ -51,15 +57,16 @@ public void Parse_PageSize_CanParse(string value, int expectedValue, bool should
}
[Theory]
- [InlineData("1", 1, false)]
- [InlineData("abcde", 0, true)]
- [InlineData("", 0, true)]
- public void Parse_PageNumber_CanParse(string value, int expectedValue, bool shouldThrow)
+ [InlineData("1", 1, null, false)]
+ [InlineData("abcde", 0, null, true)]
+ [InlineData("", 0, null, true)]
+ [InlineData("5", 5, 10, false)]
+ [InlineData("5", 5, 3, true)]
+ public void Parse_PageNumber_CanParse(string value, int expectedValue, int? maximumPageNumber, bool shouldThrow)
{
// Arrange
var query = new KeyValuePair($"page[number]", new StringValues(value));
- var service = GetService();
-
+ var service = GetService(maximumPageNumber: maximumPageNumber);
// Act
if (shouldThrow)