In this post, we’ll explore the new validation feature introduced in .NET 10 a native, first‑class validation filter built directly into Minimal APIs.
Before .NET 10, Minimal APIs encouraged a style where validation logic often lived inline inside handlers. While that approach works for small demos, it breaks down quickly in real applications. As endpoints grow, business intent becomes buried under guard clauses, validation logic gets duplicated, and error responses become inconsistent across the API surface.
.NET 10 changes this completely.
Validation is now tightly integrated with model binding and endpoint filters. When a request is bound to a parameter or complex object, validation runs automatically.
If validation fails, the request never reaches our handler.
Instead, the framework produces a standardized 400 Bad Request response with a ProblemDetails payload consistent, predictable, and aligned with the rest of ASP.NET Core.
Let’s look at some practical examples to understand how validation is implemented in Minimal APIs; all examples below assume this basic setup:
using MinimalAPIValidation;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddValidation();
var app = builder.Build();
app.Run();
VALIDATION WITH DATA ANNOTATIONS ON REQUEST BODIES
The most common form of validation is validating a JSON request body. This is done using standard data annotations.
[Request Model]
using System.ComponentModel.DataAnnotations;
namespace MinimalAPIValidation;
public class CreateUserRequest
{
[Required]
[EmailAddress]
public string Email { get; set; } = string.Empty;
[Required]
[MinLength(8)]
public string Password { get; set; } = string.Empty;
}
[Endpoint]
app.MapPost("/users", (CreateUserRequest req) =>
{
return Results.Created($"/users/{req.Email}", req);
});
[Run the API]



The endpoint handler is never executed.
ASP.NET Core automatically returns a 400 Bad Request with detailed validation errors.
VALIDATION ON ROUTE PARAMETERS
Validation in .NET 10 Minimal APIs is not limited to request bodies. Route parameters can also be validated declaratively.
[Endpoint]
app.MapGet("/products/{id:int}", (
[Range(1, int.MaxValue)] int id) =>
{
return Results.Ok(new {ProductId = id});
});
[Run the API]


Calling /products/0 immediately results in a validation error. The handler does not run.
VALIDATION ON QUERY PARAMETERS
[Endpoint]
app.MapGet("/orders", (
[FromQuery][Range(1, 100)] int pageSize = 20) =>
{
return Results.Ok(new { PageSize = pageSize });
});
[Run the API]


A request like /orders?pageSize=510 fails validation automatically.
This keeps pagination and filtering logic clean and predictable, without manual guard clauses.
CUSTOM VALIDATION WITH IValidatableObject
Some rules cannot be expressed with attributes alone. They may involve multiple properties or domain-specific logic. This is where custom validation comes in.
[Custom Validation Model]
using System.ComponentModel.DataAnnotations;
namespace MinimalAPIValidation;
public class BookingRequest : IValidatableObject
{
public DateTime StartDate { get; init; }
public DateTime EndDate { get; init; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
if (EndDate <= StartDate)
{
yield return new ValidationResult(
"EndDate must be after StartDate",
[nameof(EndDate)]);
}
}
}
[Endpoint]
app.MapPost("/bookings", (BookingRequest request) =>
{
return Results.Ok(request);
});
[Run the API]


Validation in Minimal APIs has evolved from a missing feature into a first-class design pillar in .NET 10. We can now build APIs that are explicit at the boundaries, clean in their handlers and consistent in error behavior.
If we already use Minimal APIs, upgrading to .NET 10 is an opportunity to remove boilerplate, centralize validation, and make our endpoints easier to reason about without sacrificing control.