Course outline

ASP.NET Core / Basic: HTTP and Application Composition / Lesson 4

Minimal API Binding and Results

Define input sources, DTO meaning, validation, and typed response choices without mistaking a compact handler for a complete API contract.

About 5 min readContent reviewed
On this page

Lesson goals

  • Explain route/query/body/service binding.
  • Separate binding failure from domain rejection.
  • Use explicit result types and endpoint metadata.

A handler parameter has a source contract

Minimal API handlers can bind values from route, query, header, JSON body, forms, DI services, or supported custom binding. Some sources are inferred from type, method, and registration, so an innocent signature edit can change behavior. Use explicit attributes where ambiguity would make review difficult. A request DTO is a boundary model, not automatically an EF entity.

GET and other documented methods do not implicitly bind arbitrary body parameters as POST does; bind explicitly when the contract requires a body. Keep HTTP method semantics, caching, and client support in mind rather than choosing a signature solely because it compiles. Data source and trust level remain visible responsibilities.

An endpoint declaration needs a host and supporting services

The illustrative excerpt names route and query sources directly. It remains non-runnable because app construction, service setup, request execution, and integration behavior are outside the fragment. Its DTO represents returned contract data, not a database model. The source choices are reviewable even though no server is started.

Adding a route constraint can reject candidate selection before a handler runs, while a matched handler can reject an invalid page under its domain rule. Decide the status/message shape for each category. Do not assume every invalid request reaches the same code or is automatically validated by a body annotation.

csharp Illustrative explicit binding sources
app.MapGet("/reports/{id:int}",
    ([FromRoute] int id, [FromQuery] int page) =>
        page < 1 ? Results.BadRequest("page must be positive") : Results.Ok(new { id, page }));

This application fragment is not executed and requires the relevant using directives, app, and host setup.

Binding is representation, validation is meaning

Binding can reject text that cannot become an integer or JSON that cannot deserialize. A successfully bound integer can still violate a positive-page rule. Validate shape, size, required presence, and domain relations before committing effects. Nullable annotations help static intent but do not replace checking hostile input.

Framework version and registration determine automatic validation behavior. In ASP.NET Core 10, Minimal API validation support has its own setup and contract; it should not be confused with MVC ApiController behavior. Custom endpoint filters can implement cross-cutting checks, but they do not automatically run when a handler is called directly in a unit test.

A result object makes response intent inspectable

TypedResults exposes concrete result types useful for tests and metadata. The complete example constructs an Ok result and inspects its status/value in memory. It does not execute serialization or network response delivery. A handler returning a DTO as 200 and one returning a validation result have different contracts even if both methods look short.

Use a narrow documented set of status/result alternatives. Results<T1,T2> can preserve typed alternatives in a handler signature, while IResult provides a common abstraction with fewer compile-time specifics. Choose clarity for consumers and generated API metadata rather than returning unrelated anonymous shapes on every failure path.

csharp Inspect a typed result without HTTP execution
using System;
using Microsoft.AspNetCore.Http;

var result = TypedResults.Ok(new ReportDto(7, "ready"));
Console.WriteLine($"status={result.StatusCode} id={result.Value!.Id} state={result.Value.State}");

internal sealed record ReportDto(int Id, string State);
Expected output
status=200 id=7 state=ready

The Ok object contains status 200 and the synthetic DTO; serialization and binding are not tested.

Keep API discovery aligned with runtime behavior

Endpoint names, groups, tags, result metadata, and OpenAPI descriptions help consumers understand the surface. They are not a substitute for actual validation or authorization. A description claiming 401/403/409 must match implemented behavior, and examples should not expose secrets or real personal records. Keep document generation distinct from request execution tests.

Test allowed input, missing/invalid source values, wrong methods, domain failures, typed alternatives, and error shape through the appropriate layer. Direct result tests are fast and useful, but public binding and middleware behavior need integration coverage. A small endpoint still needs ownership for cancellation, persistence, and operational effects.

Common errors and fixes

A bound DTO is assumed valid for persistence.

Why it happens
Deserialization and domain rules were conflated.
How to fix it
Validate before effects and map to the domain explicitly.
Prevent it next time
Test syntactically valid but invalid-domain values.

A handler test is expected to execute an endpoint filter.

Why it happens
Direct invocation omits the framework filter path.
How to fix it
Test the filter/pipeline separately or use a supported integration setup.
Prevent it next time
State the layer exercised by each test.

OpenAPI metadata promises failures the handler never returns.

Why it happens
Documentation and runtime contracts diverged.
How to fix it
Align result alternatives and verify their paths.
Prevent it next time
Review generated metadata with behavior tests.

Check your understanding

1. A handler accepts page as int and returns a database query immediately. What checks are still needed?

Show worked solution

Define whether missing page has a default, whether zero/negative/huge values are rejected, the maximum page size, sorting, authorization, and cancellation. Successful integer binding proves none of these. Apply bounded domain rules before executing the query and test the public status/error shape. Keep pagination stability and resource cost in the API contract.

2. What does inspecting TypedResults.Ok establish, and what remains outside that test?

Show worked solution

It establishes the constructed resultโ€™s declared status and value. It does not prove route selection, parameter binding, JSON options, response execution, authentication, authorization, or transport behavior. Keep that useful unit assertion, then add appropriate integration tests for the public contract instead of claiming the result object alone validates the endpoint.

Key takeaways

  • Input sources, representation, validation, and effects are separate.
  • Typed results improve inspectability without replacing integration checks.
  • Version-specific automatic validation needs explicit setup and evidence.

Official references