Repository navigation
OpenAPI and ApiExplorer
PagedResultApplicationModelConvention automatically annotates every controller action that returns an IPagedResult<T>. The result is a Swagger / OpenAPI document that describes paged endpoints accurately without manual [ProducesResponseType(...)] decoration.
The convention is added either by AddPagedListResultWeb (which inserts it into MvcOptions) or explicitly by AddPagedListResultApiExplorer. The two calls are idempotent — calling both is safe.
builder.Services
.AddControllers()
.AddPagedListResultApiExplorer();
builder.Services.AddPagedListResultWeb();For each action whose return type unwraps to IPagedResult<T> (the convention recognizes Task<>, ValueTask<>, ActionResult<> and direct returns), the convention adds the following filter / response metadata, but only if the user has not declared them already:
| Attribute | Purpose |
|---|---|
[Produces("application/json")] |
Sets the supported response content type. Skipped if the action or the controller already declares Produces. |
[ProducesResponseType(typeof(PagedResult<T>), 200)] |
Documents the success body schema. The generic T is resolved from the unwrapped return type. |
[ProducesResponseType(typeof(ProblemDetails), 400)] |
Documents the failure body for client validation issues. |
Existing user-declared response codes are preserved. For example, if your action already declares [ProducesResponseType(StatusCodes.Status404NotFound)], the convention will keep that and only add the missing 200/400 entries.
The unwrap order, used to find the T of IPagedResult<T>:
-
Task<X>-> recurse onX. -
ValueTask<X>-> recurse onX. -
ActionResult<X>-> recurse onX. - Otherwise: if
XimplementsIPagedResult<U>for someU, the success type isPagedResult<U>.
If the action returns a non-paged type (IActionResult, Task<IActionResult>, etc.), the convention skips the action.
Minimal-API endpoints do not go through MvcOptions and therefore are not touched by the convention. To get equivalent OpenAPI metadata, attach the standard endpoint-level helpers manually:
app.MapGet("/products", ...)
.WithPagedResult()
.Produces<PagedResult<Product>>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);Both Swashbuckle and NSwag honor the standard ASP.NET Core action-model conventions, so once the convention is registered the generated swagger.json describes paged endpoints out of the box. No additional schema filter is required.
If you customize the JSON serializer through PagedListResultWebOptions.JsonSerializerOptions, also configure your OpenAPI generator with the same naming policy so that schema property names match the wire format.
Apply your own [Produces] and [ProducesResponseType] attributes — the convention checks for their presence and will not duplicate them. To disable everything, mark the action with the standard [ApiExplorerSettings(IgnoreApi = true)] attribute.
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