← ASP.NET Core: applications, contracts and operations
03 / 10 · 60 MIN

Routing, binding and validation

Identify argument sources and the stage rejecting a request, separating route matching, conversion and validation rules.

Three boundaries before an effect

Routing decides which endpoint matches the request. Binding obtains values from declared or inferred sources and converts them to signature types. Validation checks rules on values and models that can already be represented. Do not treat the business handler as the origin of all these failures.

Consider a POST with Content-Type text/plain for a JSON DTO, one with malformed JSON and one with valid JSON but Quantity=0. The first can fail with 415; the second with binding 400; the third with validation 400 when the mechanism is enabled. The same 400 status does not mean the same cause.

Constraints select routes

Route /items/{id:int} only matches when the segment satisfies the int constraint. With no other route or custom handling, /items/blue never reaches the handler and produces 404. A response the handler might have produced for an invalid id does not participate because the endpoint was not selected.

When the contract requires an input-error 400 for that segment, separate selection from validation. Route /items/{id} with int binding allows invalid conversion to be rejected at that boundary; explicit handling can define public details. Keep lookup of a valid numeric id that does not exist distinct.

The source is part of the contract

FromRoute, FromQuery, FromHeader and FromBody make the source explicit. FromQuery(Name="p") lets the C# variable be page while the client sends p. This avoids depending on matching names and makes contract changes visible in the signature.

Inference also considers type: a complex type registered as a service can come from DI rather than the body. If SearchRequest must represent client JSON, FromBody expresses that intention. Do not attempt to repair an inferred source merely by changing the endpoint name or a header unrelated to DI.

Omission is not invalid text

An int? can represent omission. In the example, when p is absent from the query, page is null and page ?? 1 chooses the default. If p=blue is present, the value cannot be parsed as an integer; nullable does not mean every invalid text is silently replaced by null.

This distinction protects the contract: a client omitting an option can accept default behavior, while a client sending malformed data needs to correct the request. The fallback expression runs in the handler after binding. Changing that fallback does not repair a failure preventing the call.

Retain multiplicity

Query /search?tag=urgent&tag=fragile contains two values for the same key. A string[] tag parameter retains that multiplicity. Do not move these values to FromBody or FromHeader without also changing the client contract; attributes are not equivalent ways to request the same value.

Do not assume one string urgent,fragile is automatically equivalent to two occurrences of the key either. The contract must state how each value is sent and interpreted. The example returns the received array to clarify binding; it performs no real search and grants no access to objects associated with those tags.

Enable model validation

Range(1,100) on the DTO declares a rule, but execution depends on infrastructure. In ASP.NET Core 10, AddValidation enables built-in validation of supported parameters. The generator must discover models; the example keeps model, endpoint and registration call in the application assembly and assumes generated metadata.

If registration moves to another assembly, review discovery rather than assuming coverage of every type. DisableValidation disables the mechanism for a particular endpoint. These rules replace neither authorization nor validation depending on current state, such as available stock. They are also distinct from the previous lesson’s startup options validation.

Diagnose with bounded requests

Read the example as a model: /items/42?p=3 produces id=42 and page=3; without p, page=1. /items/42?p=blue fails before the handler response. For POST /requests with readable JSON, Quantity=0 is rejected by the enabled rule while Quantity=3 permits valid-in-model. That result confirms neither persistence nor delivery.

For each record, choose the first boundary preventing execution and the smallest change correcting the contract. Do not remove validation to conceal a client error. Assessments present illustrative requests and observations for response selection; they require neither code execution nor traffic capture.

// Program.cs: ASP.NET Core 10.0 Web SDK, net10.0, ImplicitUsings enabled.
// Microsoft.Extensions.Validation 10.x available; model and AddValidation
// are in this application assembly, with generated validation metadata.
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddValidation();
var app = builder.Build();
app.MapGet("/items/{id}", (int id, [FromQuery(Name = "p")] int? page) =>
    TypedResults.Ok(new { id, page = page ?? 1 }));
app.MapGet("/search", ([FromQuery] string[] tag) =>
    TypedResults.Ok(new { tags = tag }));
app.MapPost("/requests", ([FromBody] WorkRequest request) =>
    TypedResults.Ok(new { request.Quantity, state = "valid-in-model" }));
app.Run();

public sealed class WorkRequest
{
    [Range(1, 100)]
    public int Quantity { get; set; }
}
IN PRACTICE

For /items/42?p=3, id comes from the path and page from query p. Omitting p uses 1; p=blue fails conversion. For JSON POST, Quantity=0 violates Range; Quantity=3 permits the illustrative response. No order is created.

Common pitfalls

Using constraints as input validation; assuming nullable accepts any text; losing repeated parameters; relying on inference after registering a DTO in DI; adding annotations without enabling corresponding validation.

Related topics: Hosting, middleware and request flow · Responses, errors and API contracts · Authentication and authorization

Take this idea with you

First select the endpoint; then identify and convert data; finally apply acceptance rules. A failure at each boundary has its own meaning and correction.

Create account

Reference: Explicit parameter binding · ASP.NET Core 10.0 / .NET 10 / C# 14