Repository navigation
Query String Syntax
This page documents the grammar accepted by PagedRequestQueryParser, used by both the MVC binder ([FromPagedQuery]) and the minimal-API wrappers (PagedQuery<T>, PagedQueryWithFilters<T>). The parser populates a PagedRequest (or PageRequestWithFilters) instance from an IQueryCollection, optionally constrained by a PageableMetadata allow-list.
The constants are defined in PagedQueryKeys.
| Key | Bound to | Notes |
|---|---|---|
page |
PagedRequest.Page |
Integer >= 1. Falls back to 1 if missing or invalid. |
pageSize |
PagedRequest.PageSize |
Integer >= 1. Capped to PagedListResultWebOptions.MaxPageSize. Falls back to PagedListResultWebOptions.DefaultPageSize if missing. |
search |
PagedRequest.Search.Search |
Free-text term. |
searchAll |
PagedRequest.Search.SearchInAllTextFields / SearchInAllFields
|
Accepts true / text / fields (search across text fields), all (search across every property), or false / none (limit to searchFields). |
searchFields |
PagedRequest.Search.CustomSearchTextProperties |
Comma-separated list of property names. Filters out names that are not in the allow-list (when present). |
order |
PagedRequest.Order.OrderByProperty / OrderDirection / OrderByDefaultProperty
|
Format <property> or <property>:<dir> where <dir> is asc / desc. The literal value default activates OrderByDefaultProperty = true. |
filter |
PageRequestWithFilters.Filters |
Repeatable. See "Filter grammar" below. |
fields |
PagedRequest.Fields |
Comma-separated list of property names to project. |
predefinedField |
PagedRequest.PredefinedRecord.PredefinedFieldName |
Property name to match against. |
predefinedRecords |
PagedRequest.PredefinedRecord.PredefinedRecords |
Comma-separated list of identifiers. |
The parser is case-insensitive on keys and on operator names. Whitespace around values is trimmed.
filter = <propertyName> ":" <operator> [ ":" <value> [ "," <value2> ... ] ]
The operator is the textual name of a FilterType value (e.g. Equals, Between, IsIn). The number of accepted values depends on the operator:
| Operator family | Values | CompareValue |
|---|---|---|
Equals, NotEquals, StartsWith, DoesNotStartWith, EndsWith, DoesNotEndsWith, Contains, SensitiveContains, DoesNotContains, SensitiveDoesNotContains, GreaterThan, GreaterThanOrEquals, LessThan, LessThanOrEquals
|
Exactly 1 | Ignored |
Between |
Exactly 1 (lower bound) | Required (upper bound) |
IsIn, IsNotIn
|
1 or more, comma-separated | Ignored |
IsNull, IsNotNull
|
None | Ignored |
Examples:
filter=status:Equals:active
filter=price:Between:10,100
filter=tags:IsIn:new,featured,sale
filter=archivedOn:IsNull
To send several filters, repeat the filter key:
?filter=status:Equals:active&filter=price:Between:10,100
The top-level link between filters is the filterLink argument of GetPagedWithFiltersAsync (default And). To express child conditions linked to a parent (the Dependencies collection), use a body-bound request: the query string is intentionally limited to the flat case.
When a filter row cannot be parsed, the binder writes an entry into ModelStateDictionary (or into PagedQuery<T>.Errors) with the key filter[i], where i is the zero-based index of the offending row in the original query string. This makes it easy to surface a precise validation error in the response:
When PageableMetadata is registered for the bound entity (via ConfigurePageable<T>), the parser enforces:
-
orderis rejected if the property is not in theAllowedSortset. The error key isorder. -
filter[i]is rejected if the property is not in theAllowedFilterset. -
searchFieldsentries are filtered: any name not inAllowedSearchis dropped silently.
If no metadata is registered for the entity (or [FromPagedQuery] is used without EntityType), the parser does not enforce any allow-list and accepts all property names.
See Allow List Registry.
You can call the parser yourself if you need to bind from a non-HTTP source (background job, queue worker, etc.):
var request = new PageRequestWithFilters();
PagedRequestQueryParser.Populate(
request,
httpContext.Request.Query,
options: new PagedListResultWebOptions { MaxPageSize = 200 },
allowList: registry.Get(typeof(Product))); // optionalThe result is the populated request instance plus a PagedParseResult containing the per-key errors (Errors is a Dictionary<string, string> keyed by the key name described above).
Given the following request:
GET /products
?page=2
&pageSize=20
&search=usb
&searchAll=text
&order=price:desc
&filter=status:Equals:active
&filter=price:Between:10,100
&fields=Id,Name,Price
&predefinedField=Id
&predefinedRecords=1,2,3
The bound PageRequestWithFilters will look like:
{
"page": 2,
"pageSize": 20,
"search": {
"search": "usb",
"searchInAllTextFields": true,
"searchInAllFields": false,
"customSearchTextProperties": []
},
"order": { "orderByProperty": "price", "orderDirection": 1, "orderByDefaultProperty": false },
"fields": [ "Id", "Name", "Price" ],
"predefinedRecord": {
"predefinedFieldName": "Id",
"predefinedRecords": [ "1", "2", "3" ]
},
"filters": [
{ "filterValue": { "condition": 0, "propertyName": "status", "values": [ "active" ] }, "filterApplyOrder": 0 },
{ "filterValue": { "condition": 14, "propertyName": "price", "values": [ "10" ], "compareValue": "100" }, "filterApplyOrder": 1 }
]
}(condition: 14 is Between. See Data Models Reference for the full enum table.)
PagedListResult is maintained by RzR. GitHub repository.
Start here
Reference
- Data Models Reference
- Expression Builders (Common)
- EntityFramework Core Extensions
- Query String Syntax
ASP.NET Core integration
- Web Integration Overview
- MVC Integration
- Minimal API Integration
- Configuration and Options
- Allow List Registry
- Response Headers and ProblemDetails
- OpenAPI and ApiExplorer
Operations
{ "type": "https://datatracker.ietf.org/doc/html/rfc9457", "title": "One or more validation errors occurred.", "status": 400, "errors": { "filter[1]": [ "Operator 'Between' requires a CompareValue (upper bound)." ] } }