How to manually validate a model in a controller in ASP.NET Core
By FoxLearn 2/4/2025 8:39:49 AM 32
In either case, you can handle validation errors by adding them to the ModelState.
Using TryValidateModel() for attribute-based validation
If you need to validate a model based on the validation attributes defined on its properties, you can use the TryValidateModel()
method. This method will check the model's attributes and add any validation errors to the ModelState
.
[ApiController] [Route("[controller]")] public class ProductController : ControllerBase { [HttpPost] public IActionResult Post() { var product = GetProductFromRequest(Request.Body); if (!TryValidateModel(product)) { return ValidationProblem(); } // Process the valid product return Ok(); } // Rest of controller logic }
Internally, TryValidateModel()
uses ObjectValidator.Validate()
to validate the model and populate ModelState
with any errors. If validation fails, the ValidationProblem()
method returns a 400 Bad Request
response with details about the validation errors, like this:
{ "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "traceId": "00-8a8231ab7dfe2a6c67b1c61c8b62ba9b-5482fb5c38154b07-00", "errors": { "Price": [ "The field Price must be greater than 0." ] } }
Manually Adding Validation Errors to ModelState
If you want to manually perform validation logic, such as ensuring a property meets custom conditions, you can add errors directly to ModelState
.
[HttpPost] public IActionResult Post(Product product) { // Step 1 - Manually validate the product fields if (product.Price <= 0) { ModelState.AddModelError(nameof(product.Price), "Price must be greater than zero."); } // Step 2 - Return validation problem if any error exists if (!ModelState.IsValid) { return ValidationProblem(); } // Process the valid product return Ok(); }
In this example, if the Price
field is invalid (less than or equal to zero), an error is added to ModelState
using AddModelError()
. The response returned by ValidationProblem()
will look like this:
{ "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "traceId": "00-dc315a729467fca0b85f92383956f67f-09f21b627acbd8e6-00", "errors": { "Price": [ "Price must be greater than zero." ] } }
Combining Attribute Validation and Custom Logic
You don't have to choose between using validation attributes and performing custom validation.
[HttpPost] public IActionResult Post(Product product) { // Use TryValidateModel for attribute-based validation if (!TryValidateModel(product)) { return ValidationProblem(); } // Apply custom validation logic if (product.ManufactureDate > DateTime.UtcNow) { ModelState.AddModelError(nameof(product.ManufactureDate), "Manufacture date cannot be in the future."); } // Return validation error if needed if (!ModelState.IsValid) { return ValidationProblem(); } // Process the valid product return Ok(); }
Handling Automatic Validation
In ASP.NET Core, if you use the [ApiController]
attribute, automatic model validation occurs, meaning you get automatic 400 responses for invalid models. However, you may wish to handle this manually in some cases, for example, to customize the error message or to modify the model before re-validating.
To disable automatic validation, you can configure it in Program.cs
or Startup.cs
like so:
builder.Services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; });
Now you can manually check ModelState.IsValid
and return a custom error response:
[HttpPost] public IActionResult Post(Product product) { if (!ModelState.IsValid) { return BadRequest("Invalid product data."); } // Rest of the method logic }
If you want to attempt to fix the validation problem and re-validate the model, you can clear the ModelState
and update the model before calling TryValidateModel()
again:
[HttpPost] public IActionResult Post(Product product) { if (!ModelState.IsValid) { ModelState.Clear(); // Example fix: Setting a default value for the missing field product.ManufactureDate = DateTime.UtcNow.AddYears(-1); if (!TryValidateModel(product)) { return ValidationProblem(); } } // Process the valid product return Ok(); }
With these techniques, you can have full control over your model validation in ASP.NET Core, whether you're using built-in validation attributes, implementing custom logic, or even combining both.
- How to supply IOptions in ASP.NET Core
- Logging requests and responses in ASP.NET Core
- How to disable ModelStateInvalidFilter in ASP.NET Core
- How to add custom middleware in ASP.NET Core
- How to Turn Off Startup Logging in ASP.NET Core
- Dependency inject BackgroundService into controllers
- How to Configure JSON Serializer options in ASP.NET Core
- How to use Razor View Engine in ASP.NET Core