100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

Model Binding and Validation

How ASP.NET Core maps incoming request data onto action parameters and validates it before your handler code runs.

Building APIsIntermediate10 min readJul 10, 2026
Analogies

How Model Binding Works

Model binding maps data from an incoming HTTP request onto action method parameters or a bound model object. Binding source attributes, [FromBody], [FromQuery], [FromRoute], [FromForm], and [FromHeader], control exactly where a value is read from; when no attribute is present, ASP.NET Core infers a source based on the parameter's type and context, complex types default to the body in controllers and default to the body in minimal APIs too, while simple types default to route values (if a matching template segment exists) or the query string otherwise.

🏏

Cricket analogy: Model binding inferring where a value comes from is like an umpire automatically knowing whether an appeal is for LBW or a run-out based on the fielder's gesture: [FromRoute] pulls an id from the URL path the way a caught-behind appeal is read from the wicketkeeper's specific gesture, no extra explanation needed.

csharp
public class CreateOrderRequest
{
    [Required, StringLength(100, MinimumLength = 3)]
    public string CustomerName { get; set; } = default!;

    [Range(1, 1000)]
    public int Quantity { get; set; }
}

[HttpPost]
public IActionResult Create(
    [FromBody] CreateOrderRequest request,
    [FromHeader(Name = "X-Client-Id")] string clientId)
{
    if (!ModelState.IsValid)
        return ValidationProblem(ModelState);

    return Ok(new { request.CustomerName, request.Quantity, clientId });
}

Data Annotations Validation

Data annotation attributes, [Required], [StringLength], [Range], [EmailAddress], and others, are applied directly to model properties and are evaluated automatically during model binding, populating ModelState with any validation errors before the action method body runs. On a controller decorated with [ApiController], the framework short-circuits the action entirely and returns a 400 Bad Request with a ProblemDetails body the moment ModelState.IsValid is false, without the developer needing to check it manually.

🏏

Cricket analogy: Data annotations are like a bowling-action review that checks an arm doesn't bend beyond 15 degrees before a delivery is legal: [Range(0,100)] on a Score property rejects an invalid value the way ICC's biomechanics check rejects an illegal bowling action before the ball is bowled.

When a controller is decorated with [ApiController], ASP.NET Core automatically short-circuits the action and returns a 400 Bad Request with a ProblemDetails body the moment ModelState.IsValid is false; you don't need to write the if (!ModelState.IsValid) return BadRequest(...) check yourself in most cases.

Custom Validation

Cross-property validation rules, ones that depend on the relationship between two or more fields, can't be expressed by a single data annotation attribute on one property. Implementing IValidatableObject on a model, or writing a custom ValidationAttribute subclass, lets you inspect the whole object and add validation errors that reflect relationships, such as requiring a CheckOutDate to fall after a CheckInDate.

🏏

Cricket analogy: A custom validator implementing IValidatableObject is like a third umpire applying a nuanced rule that depends on multiple factors at once, such as checking both foot placement and bat position for a stumping: it can compare EndDate against StartDate across the whole object, something a single [Range] attribute on one property can't do.

FluentValidation as an Alternative

FluentValidation is a popular third-party library that externalizes validation rules into dedicated validator classes, subclasses of AbstractValidator<T>, using a fluent chain like RuleFor(x => x.Email).NotEmpty().EmailAddress(), instead of decorating the model with attributes. Because a validator is just a class registered in DI, it can inject dependencies like a repository to perform asynchronous checks (uniqueness, for example), and it's straightforward to unit test in isolation from ASP.NET Core's request pipeline.

🏏

Cricket analogy: FluentValidation is like a specialist fielding coach brought in separately from the head coach to focus purely on catching drills: instead of annotating the Player class itself with attributes, you write a dedicated PlayerValidator class, keeping validation logic cleanly separated the way a specialist coach's drills are separate from match tactics.

Binding a request body directly onto an EF Core entity (rather than a dedicated DTO) opens the door to overposting: a client can include extra JSON properties like IsAdmin or AccountBalance that map onto entity properties never intended to be client-settable. Always bind to a purpose-built request DTO and map explicitly onto the entity.

  • Binding source attributes like [FromBody], [FromQuery], [FromRoute], [FromForm], and [FromHeader] control exactly where a parameter's value is read from.
  • When a source isn't specified, ASP.NET Core infers it: complex types default to the body, simple types default to the query string.
  • Data annotations like [Required], [StringLength], and [Range] populate ModelState, and [ApiController] auto-returns 400 when it's invalid.
  • IValidatableObject and custom ValidationAttribute subclasses support cross-property rules a single per-field attribute can't express.
  • FluentValidation externalizes validation rules into dedicated, independently testable validator classes instead of attributes on the model.
  • Binding a request body directly onto an EF Core entity risks overposting; always bind to a dedicated DTO.

Practice what you learned

Was this page helpful?

Topics covered

#ASPNETCoreStudyNotes#MicrosoftTechnologies#ModelBindingAndValidation#Model#Binding#Validation#Works#StudyNotes#SkillVeris#ExamPrep