Skip to main content

How to Validate Minimal API Requests in .NET 10 C# with AddValidation

· 6 min read
Jagdish Kumawat
Founder @ Dewiride

Before .NET 10, minimal APIs didn't check data annotations: you needed a filter or a library. Now one line in Program.cs does it. Put [Required] and [Range] on your request record, and a bad request gets a 400 with every problem listed, before your handler runs. This guide adds it, with the same requests before and after.

Prerequisites​

Step 1: See What Happens Without It​

The record already has attributes, and without AddValidation nothing reads them. An empty name goes straight into the database:

Response (POST /api/v1/categories with an empty name)
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: /api/v1/categories/504

{
"id": 504,
"name": "",
"createdAtUtc": "2026-11-04T11:44:18.818152Z"
}

A negative price only stops at MySQL's check constraint, as a 500:

Response (POST /api/v1/products with a negative price)
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
"title": "An error occurred while processing your request.",
"status": 500,
"traceId": "00-872aa012421c5768359f1c2a2b94c9ca-c1556660a715255a-00"
}

Step 2: Turn It On​

src/Dewiride.Inventory.Api/Program.cs (excerpt)
// Checks the validation attributes on request records before an endpoint runs.
builder.Services.AddValidation();

ASP.NET Core's validation section says what that covers: "Enabling validation allows the ASP.NET Core runtime to perform validations defined on the" query, the header and the request body, and "When a parameter to a Minimal API endpoint is a class or record type, validation attributes are automatically applied."

Step 3: Describe the Rules on the Record​

src/Dewiride.Inventory.Api/Features/Categories/CategoryContracts.cs (excerpt)
using System.ComponentModel.DataAnnotations;

namespace Dewiride.Inventory.Api.Features.Categories;

/// <summary>What a client sends to create or rename a category.</summary>
public sealed record CategoryRequest(
[Required, StringLength(100)] string Name);

Step 4: Send the Same Request​

Response
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Name": [
"The Name field is required."
]
},
"traceId": "00-3a3b5e2d5ee9c88e729df453bd8b324b-771939a406ecf6bf-00"
}

A terminal with a 400 Bad Request response in problem details format, with an errors object listing the Name field and its message.

The page says: "If validation fails, the runtime returns a 400 - Bad Request response with details of the validation errors." The handler never ran, so the database wasn't touched. The errors object has one list of messages per field, which a client can show next to the right input.

Step 5: Validate Items Inside a List​

It also goes into nested records and lists. This request has an order with lines:

A temporary set of records
public sealed record OrderLineRequest(
[Range(1, int.MaxValue)] int ProductId,
[Range(1, 1000)] int Quantity);

public sealed record PlaceOrderRequest(
[Required, StringLength(200)] string CustomerName,
[Required, EmailAddress] string CustomerEmail,
[Required, MinLength(1), MaxLength(50)] List<OrderLineRequest> Lines);

With a bad email and two bad lines, every problem comes back in one response, with its path:

Response
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"CustomerEmail": [
"The CustomerEmail field is not a valid e-mail address."
],
"Lines[0].Quantity": [
"The field Quantity must be between 1 and 1000."
],
"Lines[1].ProductId": [
"The field ProductId must be between 1 and 2147483647."
]
},
"traceId": "00-57fb7c6624e253b709d99b041c7e0e5c-dfeebc860523f6b7-00"
}

An empty list is caught by MinLength:

Response
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Lines": [
"The field Lines must be a string or array type with a minimum length of '1'."
]
},
"traceId": "00-985555ec696997d3198ebacc5b1ee4c5-4f6ad1b584870bbe-00"
}

Step 6: Turn It Off for One Endpoint​

If an endpoint shouldn't validate, say so on the route:

A temporary endpoint
d.MapPost("/no-validation", (PlaceOrderRequest request) =>
Results.Ok(new { request.CustomerName })).DisableValidation();
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"customerName": ""
}

What It Doesn't Check​

Attributes check the request by itself. They can't know what's in your database, so a name that already exists, or a product that doesn't, is for your code to handle, such as a 409 for a duplicate. Rules of your own go in a custom attribute.

Common Mistakes​

MistakeWhat happensDo this instead
Forgetting AddValidationAttributes do nothing, and bad data reaches the databaseRegister it
A decimal with [Required]A missing value becomes 0 and passesUse decimal?
Attributes on an [AsParameters] record's parameters/openapi/v1.json failsUse [property: Range(…)]
Checking database rules with attributesThey can't see the databaseHandle those in the handler
The C# required keyword on a request propertyA missing value gives a JSON error, not this listUse a nullable parameter with [Required]

FAQ​

Does it work on query strings?​

Yes, including a record with [AsParameters].

Does it work with older versions of .NET?​

AddValidation for minimal APIs arrived with .NET 10. Earlier versions need your own filter or a package.

Conclusion​

Call builder.Services.AddValidation(), put data annotations on your request records, and bad requests get a 400 with every problem listed, including inside lists, before your handler runs. Handle anything that needs the database in the handler.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.