What is model binding and validation in ASP.NET Core?
Quick Answer
Model binding maps incoming HTTP data (route values, query string, form, body, headers) to action parameters and complex types automatically. Validation then checks the bound model against data-annotation attributes (or custom rules), surfacing results via `ModelState.IsValid`. Together they turn raw requests into validated, strongly-typed inputs; `[ApiController]` automatically returns 400 on invalid models.
Detailed Answer
Model binding is the process of mapping HTTP request data to action method parameters. Validation ensures that the bound data meets specified constraints before processing.
4.6.1. Model Binding
Model binding automatically extracts values from:
- Form data - POST form submissions
- Route values - URL segments like
/users/{id} - Query strings - URL parameters like
?name=John&age=30 - Request body - JSON, XML payloads
- Headers - HTTP headers
Binding sources:
public class UsersController : ControllerBase
{
// From route
[HttpGet("{id}")]
public IActionResult GetUser([FromRoute] int id) { }
// From query string
[HttpGet]
public IActionResult Search([FromQuery] string name, [FromQuery] int? age) { }
// From body (JSON)
[HttpPost]
public IActionResult Create([FromBody] UserDto user) { }
// From form
[HttpPost]
public IActionResult Upload([FromForm] IFormFile file) { }
// From header
[HttpGet]
public IActionResult Get([FromHeader(Name = "X-API-Key")] string apiKey) { }
}
4.6.2. Model Validation
Validation uses data annotations to define rules. ASP.NET Core automatically validates models before action execution.
Common validation attributes:
public class UserDto
{
[Required(ErrorMessage = "Name is required")]
[StringLength(100, MinimumLength = 2)]
public string Name { get; set; }
[Required]
[EmailAddress(ErrorMessage = "Invalid email format")]
public string Email { get; set; }
[Range(18, 120, ErrorMessage = "Age must be between 18 and 120")]
public int Age { get; set; }
[Phone]
public string PhoneNumber { get; set; }
[Url]
public string Website { get; set; }
[RegularExpression(@"^[A-Z]{2}\d{6}$", ErrorMessage = "Invalid format")]
public string Code { get; set; }
[Compare("Email", ErrorMessage = "Emails must match")]
public string ConfirmEmail { get; set; }
[CreditCard]
public string CardNumber { get; set; }
}
Checking validation in controller:
[HttpPost]
public IActionResult CreateUser([FromBody] UserDto user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Process valid model
return Ok();
}
Custom validation attribute:
public class FutureDateAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(
object value,
ValidationContext validationContext)
{
if (value is DateTime date)
{
if (date > DateTime.Now)
{
return ValidationResult.Success;
}
return new ValidationResult("Date must be in the future");
}
return new ValidationResult("Invalid date");
}
}
// Usage
public class EventDto
{
[FutureDate]
public DateTime EventDate { get; set; }
}
Fluent validation (alternative approach):
// Install: FluentValidation.AspNetCore
public class UserDtoValidator : AbstractValidator
{
public UserDtoValidator()
{
RuleFor(x => x.Name)
.NotEmpty()
.Length(2, 100);
RuleFor(x => x.Email)
.NotEmpty()
.EmailAddress();
RuleFor(x => x.Age)
.InclusiveBetween(18, 120);
}
}
Automatic validation with API behavior:
// In Program.cs - ASP.NET Core 2.1+ automatically returns 400 for invalid models
builder.Services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressModelStateInvalidFilter = false; // Default
});
Key points:
- Model binding happens automatically based on parameter names and types
- Validation attributes are checked before the action executes
ModelState.IsValidcontains validation results- For APIs, ASP.NET Core automatically returns 400 Bad Request for invalid models
- Custom validation can be created via attributes or FluentValidation library