Click here to Skip to main content
15,894,343 members
Articles / Web Development / ASP.NET / ASP.NET Core

ASP.NET Core 2.0 MVC Model Validation

Rate me:
Please Sign up or sign in to vote.
1.91/5 (3 votes)
1 Sep 2017CPOL3 min read 20.9K   4   1
How to validate input models in ASP.NET Core MVC. Continue reading...

Problem

How to validate input models in ASP.NET Core MVC.

Solution

We discussed model binding in a previous post, using that sample as a template, add Save() method in HomeController:

C#
[HttpPost]
        public IActionResult Save(EmployeeInputModel model)
        {
            if (ModelState.IsValid)
                return Ok(model);

            return BadRequest(ModelState);
        }

Add a model annotated with various validation attributes:

C#
public class EmployeeInputModel
    {
        public int Id { get; set; }

        [Required]
        public string EmployeeNo { get; set; }

        [StringLength(10)]
        [MinLength(3)]
        public string Surname { get; set; }

        [EmailAddress]
        public string Email { get; set; }

        [Url]
        public string BlogUrl { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "Date of Birth")]
        public DateTime BirthDate { get; set; }

        public Gender Gender { get; set; }

        [Range(0, 10000.00)]
        public decimal Salary { get; set; }

        public bool IsPartTime { get; set; }

        public AddressInputModel Address { get; set; }
    }

    public class AddressInputModel
    {
        [Required]
        [RegularExpression("[A-Za-z0-9].*")]
        public string Line { get; set; }
    }

Discussion

Data coming from web (forms, HTTP request, etc.) must be validated. No exceptions! Model Validation is the mechanism ASP.NET Core MVC provides for this purpose. Validation triggers after the Model Binding but before the action executes.

Validations can be added by using Validation Attributes. Framework provides a lot of useful validation attributes and we can even create our own too.

Model State

ControllerBase provides a property ModelState to work with model validation. MVC will populate this dictionary with validation errors after model binding. You could then verify its IsValid property in the action method to act accordingly.

ModelState has a method to add errors to the model at server side (e.g. in Controller). This is used for validations that require database access and report errors back to the client. Modified Save() method demonstrates this:

C#
[HttpPost]
        public IActionResult Save(EmployeeInputModel model)
        {
            // simulate DB call to check existence of Id
            if (model.Id == 1)
                ModelState.AddModelError("Id", "Id already exist");

            if (ModelState.IsValid)
                return Ok(model);

            return BadRequest(ModelState);
        }

ModelState:

model state

Validation Attributes

Framework provided validation attributes exist in System.ComponentModel.DataAnnotations. In the sample application, I’ve demonstrated the use of commonly used attributes.

An interesting aspect of model validation is that even without any attributes, MVC will do some basic validations based on data types. Below is the result of a request when model has no validation and no data was supplied, note that empty values fail for Integer, DateTime and Boolean properties:

error

Custom Validation

Custom validation attributes are useful to create reusable and application specific validation logic. There are two ways to implement this (both can be used on the same model):

Inherit ValidationAttribute and Override its IsValid Method

IsValid takes in a parameter of type ValidationContext, which has few useful properties:

  • ObjectInstance: Returns the model on which the attribute is applied. Casting this to a specific model class gives you access to its properties.
  • DisplayName: Returns either the name of model’s property or string specific in [Display(Name = “…”)] attribute applied to the property.

ValidationAttribute class also provides few useful properties and methods:

  • ErrorMessage: Returns the error message set on the attribute when declaring it in the model.
  • FormatErrorMessage: Returns the generic message, i.e., the field {0} is invalid, where {0} is replaced with DisplayName.

IsValid method returns ValidationResult class, either through its static field Success or by initializing a new instance (in case of an error).

Below is a custom validation attribute applied on the BirthDate property of our model:

C#
[DataType(DataType.Date)]
        [AgeCheck]
        [Display(Name = "Date of Birth")]
        public DateTime BirthDate { get; set; }

    public class AgeCheck : ValidationAttribute
    {
        protected override ValidationResult IsValid(
object value, ValidationContext validationContext)
        {
            var model = validationContext.ObjectInstance as EmployeeInputModel;

            if (model == null)
                throw new ArgumentException("Attribute not applied on Employee");

            if (model.BirthDate > DateTime.Now.Date)
                return new ValidationResult(GetErrorMessage(validationContext));

            return ValidationResult.Success;
        }

        private string GetErrorMessage(ValidationContext validationContext)
        {
            // Message that was supplied
            if (!string.IsNullOrEmpty(this.ErrorMessage))
                return this.ErrorMessage;

            // Use generic message: i.e. The field {0} is invalid
            //return this.FormatErrorMessage(validationContext.DisplayName);

            // Custom message
            return $"{validationContext.DisplayName} can't be in future";
        }
    }

Implement IValidatableObject in Your Model

To implement more complex validations that need access to more than one property in your model, IValidatableObject works best, by implementing its Validate() method. A key thing to remember is that these validations will run after attribute based validations are successful.

Validate() method returns IEnumerable, because this method can validate more than one property of the model providing a validation error for each.

Here is a sample for our example model:

C#
public class EmployeeInputModel : IValidatableObject
    {
        ...

        public IEnumerable<ValidationResult> Validate(
            ValidationContext validationContext)
        {
            if (!string.IsNullOrEmpty(BlogUrl) &&
                    BlogUrl.Contains("tahirnaushad.com"))
                yield return new ValidationResult(
                    "URL already taken", new[] { "BlogUrl" });
        }
    }

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
PraiseGood post Pin
HHerzl24-Nov-17 2:13
HHerzl24-Nov-17 2:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.