Overview
- Model state represents errors that come from either the model binding or model validation subsystems.
- Model validation occurs after model binding and reports errors where the data does not conform to business rules (like a 0 in a field that expects a value from 1 to 5).
- Both model binding and model validation occur before a controller action method, or a Razor Pages handler method, executes.
- When a validation error occurs, model validation generates a
ModelStateDictionary
with the property name as the error key. - For web apps,
the app must inspect and react appropriately.ModelState.IsValid
Example for MVC app (controllers and views):
public async Task<IActionResult> Create(Movie movie)
{
if (!ModelState.IsValid)
return View(movie);
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Note: Web API controllers do not need to check ModelState.IsValid
if they are decorated with [ApiController]
— assuming
automatic HTTP 400 responses are enabled (default).
repeating validation
Although validation is automatic, it can be re-run if needed by calling ModelStateDictionary.ClearValidationState
and then TryValidateModel
:
public async Task<IActionResult> OnPostTryValidateAsync()
{
var modifiedReleaseDate = DateTime.Now.Date;
Movie.ReleaseDate = modifiedReleaseDate;
ModelState.ClearValidationState(nameof(Movie));
if (!TryValidateModel(Movie, nameof(Movie)))
return Page();
_context.Movies.Add(Movie);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
validation attributes
Use to define validation rules for model properties:
public class Movie
{
// ...
[ClassicMovie(1960)]
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; } = null!;
[Range(0, 999.99)]
public decimal Price { get; set; }
// ...
}
See also: System.ComponentModel.DataValidation
error messages
Validation attributes allow for defining the error message that is displayed on invalid input:
// {0} is the property name; {2} is MinimumLength and {1} is StringLength:
[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]
use json property names in validation errors
Configure like this. Note that the JsonNamingPolicy is optional:
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.ModelMetadataDetailsProviders.Add(
new SystemTextJsonValidationMetadataProvider(JsonNamingPolicy.CamelCase));
});
// ...
[Required]
Attribute and Non-nullable Reference Types
MVC validates non-nullable properties or parameters as if they had been decorated with [Required(AllowEmptyStrings = false)]
.
For example, consider this model:
public class Person
{
public string Name { get; set; }
}
A missing value for Name
yields a validation error.
To disable this behavior:
builder.Services.AddControllers(
options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
[Required]
Validation on the Server
On the server, a required value is considered missing if the property is null. [Required]
attribute’s error message is
However, The value '' is invalid
. To customize this message, either make the field nullable, or:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(_ => "The field is required.");
});
builder.Services.AddSingleton
<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
[Required]
Validation on the Client
On the client:
- A value is considered present only if input is entered (including whitespace).
- Validation handles non-nullable types the same as nullables.
So, you get client-side validation even without the [Required]
attribute.
[Remote]
Attribute
Implements client-side validation that requires calling a method on the server to determine whether field input is valid.
Documentation: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-7.0#remote-attribute
custom attributes
Use custom attributes in scenarios where built-in attributes are insufficient.
Documentation: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-7.0#custom-attributes
Top-Level Node Validation
Model-bound top-level nodes (action parameters; controller properties; page handler parameters; page model properties) are validated in addition to model properties.
Documentation: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-7.0#top-level-node-validation
Maximum Errors, Maximum Recursion
Validation stops after the maximum number of validation errors is reached (default=200). To configure:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxModelValidationErrors = 50;
});
// ...
For models that are deep or infinitely recursive, validation may result in a stack overflow. The validation depth (default=32) can be configured:
builder.Services.AddRazorPages()
.AddMvcOptions(options =>
{
options.MaxValidationDepth(128);
});
// ...
Client-side Validation
Prevents submission until the form is valid. There is a jQuery Unobtrusive Validation script that is a custom Microsoft library built on the jQuery Validation plug-in. Prevents having to code validation logic both server side and client side.
Documentation: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-7.0#client-side-validation