Skip to content

OpenAPI and ApiExplorer

RzR edited this page May 1, 2026 · 1 revision

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.

Registration

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();

What gets attached

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.

Resolving the response type

The unwrap order, used to find the T of IPagedResult<T>:

  1. Task<X> -> recurse on X.
  2. ValueTask<X> -> recurse on X.
  3. ActionResult<X> -> recurse on X.
  4. Otherwise: if X implements IPagedResult<U> for some U, the success type is PagedResult<U>.

If the action returns a non-paged type (IActionResult, Task<IActionResult>, etc.), the convention skips the action.

Minimal-API endpoints

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);

Combining with Swashbuckle / NSwag

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.

Disabling the convention for a specific action

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.

Clone this wiki locally