Model Validations in ASP.NET Core MVC

Model Validations in ASP.NET Core MVC

In this article, I will discuss Model Validations in ASP.NET Core MVC with Examples. Please read our previous article discussing Data Annotations in ASP.NET Core MVC.

What are Validations?

Validations in a web application refer to the processes and techniques used to ensure that data entered by users into web forms is accurate, secure, and appropriate before being processed or stored in the database. The main purpose of validations is to maintain data integrity, enhance user experience, and secure the website against malicious inputs. There are three types of validations:

  • Server-Side Validations
  • Client-Side Validations
  • Database Validations
Client-Side Validation:

This type of validation is performed on the user’s device, typically within the browser. It uses JavaScript or similar technologies (jQuery) to validate data as the user fills out the form. This is useful for providing immediate feedback to users about the validity of their data (like formatting errors in email addresses or passwords). However, we cannot solely depend upon it since the client-side validations can be bypassed if a user disables JavaScript or manipulates the client-side code. The following are the key points:

  • It occurs in the user’s browser.
  • Uses JavaScript (or similar client-side scripting languages) to validate data before it’s sent to the server.
  • It provides a responsive user experience because validation feedback is immediate, without requiring a round trip to the server.
  • We should never solely depend on client-side validations since users can bypass or manipulate client-side scripts.
Server-Side Validation:

This validation occurs once the data has been submitted to the server. It is important for security and data integrity because it checks the data against a broader range of criteria and rules that end users can’t easily bypass. This includes checking the uniqueness of a username, verifying user credentials, or performing more complex checks such as compliance with business rules. The following are the key points:

  • It occurs on the web server after the data is sent from the client’s browser.
  • Uses server-side scripting languages (like C#, Java, Python, PHP, etc.) to validate the data before processing it or saving it to a database.
  • While client-side validation can improve user experience by providing immediate feedback, it can be bypassed or disabled. Server-side validation acts as a necessary fallback to ensure data correctness and security.
  • It helps prevent common attacks such as SQL injection, cross-site scripting (XSS), and other forms of data tampering. By validating the inputs, the server ensures that executable code or malicious scripts are not injected into the database or displayed to other users.
Database Validation:

This involves setting constraints in the database that prevent invalid data from being stored. For example, setting a column as unique to avoid duplicate entries or defining a foreign key relationship to ensure consistency across tables. Database validations are the last line of defense for data integrity.

Why do we Need Data Annotation Attributes in ASP.NET Core MVC for Validation?

Nowadays, it’s challenging for a web developer to validate user input for any Web application. As web developers, we not only validate the business data on the client side, i.e., in the browser, but we also need to validate the business data on the server side. That means we need to validate the business data on the client and server sides.

Client-side validation gives users immediate feedback on the information they enter into a web page, which is an expected feature in today’s web applications. Along the same line, server-side validation is in place because we never trust the information coming over the network or from the client.

Data Annotations in ASP.NET Core MVC support both Client-Side and Server-Side Validation. In addition to Data Annotation, we can also use Fluent API Validations, which gives us more control than Data Annotation.

Common Validation Scenarios Encountered on Websites:

The following are the common validation scenarios encountered in a website.

  • Required Fields: Ensuring that mandatory fields are filled in before form submission.
  • Data Type Validation: Verify that the data entered is of the correct type, e.g., numbers, text, and dates.
  • Range Validation: Checking if a numeric value falls within a certain range. For instance, age should be between 18 and 60.
  • Format Validation: Verifying if data matches a particular format, e.g., email addresses, phone numbers, and postal codes.
  • Consistency Check: Ensuring that data across multiple fields is consistent. For instance, the “Confirm Password” field should match the “Password” field. Another example you can take when entering the CreditCard Number.
  • Custom Business Rules: Certain validations might be unique depending on the application’s business logic. For example, a flight booking system might validate that the return date is after the departure date.
  • Blacklist Checks: This involves checking inputs against known malicious or undesired inputs. It’s a basic defense against code injection attacks. Suppose you want to restrict some of the characters entered on a field.
  • Whitelist Checks: Only allow a specific set of values or patterns for input. It’s generally more secure than blacklist checks because it’s restrictive. It is just the opposite of Blacklist Checks.
  • Size Limit Checks: This ensures that data, especially uploaded files, don’t exceed a certain size. When entering an image, you need to set its size.
  • CAPTCHAs: To validate that the user is a human, not a bot.
Built-in Data Annotations Attributes for Model Validation in ASP.NET Core MVC:

The System.ComponentModel.DataAnnotations assembly has many built-in validation attributes that can be used for validations. They are as follows:

  • [Required]: This attribute is used to mark a property as mandatory. The MVC framework will not consider a model valid unless this property has been supplied.
  • [MinLength(length)] and [MaxLength(length)]: These attributes specify the maximum and minimum lengths for a string property, respectively.
  • [StringLength(maxLength, MinimumLength = minLength)]: This attribute sets the maximum length of a string property and optional minimum length. It is commonly used to validate input data such as names, descriptions, etc. 
  • [Range(min, max)]: This attribute restricts a property to a certain range of values. It is applicable to numerical data where you need to set minimum and maximum values.
  • [EmailAddress]: Checks that a property has a valid email format.
  • [RegularExpression(pattern)]: This attribute ensures that the property value matches a specified regular expression, which is useful for validating formats like phone numbers, zip codes, etc.
  • [Compare(“OtherProperty”)]:  This attribute is used to compare two properties of a model, often used to confirm passwords, emails, and other fields that require verification. 
  • [DataType(type)]: This attribute specifies the type of data, such as Date, Time, Email Address, URL, Credit Card, etc. It can help choose the right input field and can also assist in client-side validation.
Example to Understand Model Validation in ASP.NET Core MVC

Let’s understand Model Validation in an ASP.NET Core MVC Application using Data Annotations with one example. We want to create the following Employee Form.

Model Validations in ASP.NET Core MVC with Examples

We want to make the Name, Email, and Department fields required. If the required values are not provided and the form is submitted, we want to display the required validation errors, as shown in the image below.

Model Validations in ASP.NET Core MVC with Examples

If we enter an Invalid Email, it should display an Invalid Email Format validation error, as shown below.

Example to Understand Model Validation in ASP.NET Core MVC

Let us proceed and see how we can implement this using Data Annotation Attributes in ASP.NET Core MVC Application.

Creating the Model with Data Annotation Attributes:

Create a class file named Department.cs, then copy and paste the following code. As you can see, this will be an Enum, and these Enum constants will be the data source for our department field.

namespace DataAnnotationsDemo.Models
{
    public enum Department
    {
        None,
        HR,
        Payroll,
        IT
    }
}

Next, create another class file named Employee.cs and copy and paste the following code. As you can see in the code below, we have marked the Name, Email, and Department properties with the Required Attribute, which makes these properties required while performing the model validation. The Required Data Annotation Attribute belongs to the System.ComponentModel.DataAnnotations namespace.

using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class Employee
    {
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        [Required]
        public string Email { get; set; }
        [Required]
        public Department? Department { get; set; }
    }
}
Creating Employee Controller:

Create a new Empty – MVC Controller with the name EmployeeController and then copy and paste the following code into it.

using DataAnnotationsDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace DataAnnotationsDemo.Controllers
{
    public class EmployeeController : Controller
    {
        public IActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Create(Employee employee)
        {
            //Check if the Model State is Valid
            if (ModelState.IsValid)
            {
                //Save the Data into the Database
                //Redirect to a Different View
                return RedirectToAction("Successful");
            }

            //Return to the same View and Display Model Validation error
            return View();
        }

        public string Successful()
        {
            return "Employee Addedd Successfully";
        }
    }
}
Explanation:
  • In our example, the Create() action method, decorated with HTTP Post Attribute, will be executed when the form is submitted.
  • The model for the Create Employee Form is the Employee class. When the form is submitted, model binding maps the posted form values to the respective properties of the Employee class.
  • With the Required attribute on the Name, Email, and Department properties of the Employee class, if a value for these properties is not present, the model validation fails.
  • We need to use ModelState.IsValid property to check if validation has failed or succeeded. If validation has failed, we return the same view so the user can provide the required data and resubmit the form. If there is no validation error, we often save the data into the database and then redirect to a different view as per the business requirement.
What is ModelState.IsValid in ASP.NET Core MVC?

ModelState.IsValid in ASP.NET Core MVC is a property that indicates whether the data posted to a server is valid according to the data annotations and validation rules set on the server-side models. This property in controller actions ensures that the data sent by the client is valid. Here is how ModelState.IsValid works:

  • Data Annotation Validation: ASP.NET Core models can be decorated with data annotations that define validation rules. For example, you can use annotations like [Required], [StringLength], [Range], etc to provide the Validation Rules for a Model property.
  • Automatic Validation Check: When a request is made to an ASP.NET Core controller action, the framework automatically binds incoming request data to action parameters and checks these against the model’s validation rules. This process populates the ModelState.
  • ModelState Dictionary: The ModelState is a property of the controller base class, represented as a dictionary containing the model’s state and the validation errors. It includes information about each field (like whether there were any errors during conversion, formatting, or validation based on the rules).
  • Checking ModelState.IsValid: In the controller actions, we need to check the ModelState.IsValid property to determine if the data passed to the action meets all the validation criteria defined by the model. If ModelState.IsValid returns false, which means that at least one of the validation rules was not satisfied.
  • Handling Validation Errors: If ModelState.IsValid is false. We typically need to return to the same view in which the user entered data and display the validation error messages stored in the ModelState. This helps the user correct their input data and resubmit the form.
Displaying Model Validation Error in ASP.NET Core MVC Razor Views:

We need to use asp-validation-for and asp-validation-summary tag helpers to display model validation errors in Views. The asp-validation-for tag helper displays a validation message for a single property of our model class. On the other hand, the asp-validation-summary tag helper summarizes all validation errors that occurred on a model.

For example, to display the validation error associated with the Name property of the Employee class, we need to use asp-validation-for tag helper on a <span> element, as shown below.

<div class="form-group row">
    <label asp-for="Name" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
        <input asp-for="Name" class="form-control" placeholder="Enter Your Name">
        <span asp-validation-for="Name"></span>
    </div>
</div>

On the other hand, to display a summary of all validation errors, we need to use the asp-validation-summary tag helper on a <div> element, as shown below.

<div asp-validation-summary="All" class="text-danger"></div>

The value for the asp-validation-summary tag helper can be any of the following.

  • All: When you set the asp-validation-summary attribute to All, it displays all validation messages. This includes messages that are associated with model properties as well as messages that are added to the ModelState without a key (often used for summary error messages that aren’t tied to a specific field).
  • ModelOnly: Setting the asp-validation-summary attribute to ModelOnly will display only those validation messages that are directly associated with the model properties. This setting excludes any error messages that are added to the ModelState without a specific key.
  • None: When the asp-validation-summary is set to None, no validation summary is displayed. This option effectively disables the validation summary display. 

Let us use All. By default, it will show the error message in black color. If you want to display the error message in red, use the bootstrap class=”text-danger” as follows.

<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group row">
    <label asp-for="Name" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
        <input asp-for="Name" class="form-control" placeholder="Enter Your Name">
        <span asp-validation-for="Name" class="text-danger"></span>
    </div>
</div>
Creating ASP.NET Core Razor Views to Display Error Messages:

Next, create a view for the Create action method of Employee Controller. Once you add the Create.cshtml view file, please copy and paste the following code.

@model DataAnnotationsDemo.Models.Employee

@{
    ViewData["Title"] = "Create";
}

<h1>Create Employee</h1>

<div class="row">
    <form asp-controller="Employee" asp-action="Create" method="post" class="mt-3">
        <div asp-validation-summary="All" class="text-danger"></div>
        <div class="form-group row">
            <label asp-for="Name" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <input asp-for="Name" class="form-control" placeholder="Enter Your Name">
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
        </div>
        <div class="form-group row">
            <label asp-for="Email" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <input asp-for="Email" class="form-control" placeholder="Enter Your Email">
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
        </div>

        <div class="form-group row">
            <label asp-for="Department" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <select asp-for="Department" class="form-select" 
                        asp-items="Html.GetEnumSelectList<Department>()">
                    <option value="">Please Select</option>
                </select>
                <span asp-validation-for="Department" class="text-danger"></span>
            </div>
        </div>

        <div class="form-group row">
            <div class="col-sm-10">
                <button type="submit" class="btn btn-primary">Create</button>
            </div>
        </div>
    </form>
</div>

Now, run the application, navigate to /Employee/Create URL, and submit the form without filling in any data, and you will see the following error messages.

Creating ASP.NET Core Razor Views to Display Error Messages

Customizing Model Validation Error Message in ASP.NET Core MVC

By default, the Required attribute on the Name, Email, and Department properties displays the following default validation error messages.

  • The Name field is required.
  • The Email field is required.
  • The Department field is required.

If you want to customize the validation error message, use the ErrorMessage property of the Required attribute. Modify the Employee model class, as shown below, to display a user-friendly error message when the user submits the form without providing values for these fields.

using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class Employee
    {
        public int Id { get; set; }
        [Required(ErrorMessage = "Please Enter the First Name")]
        public string Name { get; set; }
        [Required(ErrorMessage = "Please Enter the Email Address")]
        public string Email { get; set; }
        [Required(ErrorMessage = "Please Select the Department")]
        public Department? Department { get; set; }
    }
}

With the above model changes, run the application, navigate to the /Employee/Create URL, and submit the form without filling in any data. You will see the following custom error messages as expected.

Customizing Model Validation Error Message in ASP.NET Core MVC

Using Multiple Validation Attributes on a Single Model Property:

It is also possible in ASP.NET Core to apply multiple validation attributes on a single property. For example, we have only applied the Required Attribute with the Email Property. So, in this case, it will only check whether you are providing any value for the Email Field. It is not going to check whether the entered value is in the proper format. So, if you enter anything and submit the form, it will not give you any error message, as shown in the below image.

Using Multiple Model Validation Attributes

Along with the Required Validation, we want to apply the Invalid Format error message for the Email ID field. To achieve this, we must use either the EmailAddress Attribute or RegularExpression attribute along with the Required Attribute. So, modify the Employee class as follows.

using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class Employee
    {
        public int Id { get; set; }
        [Required(ErrorMessage = "Please Enter the First Name")]
        public string Name { get; set; }
        [Required(ErrorMessage = "Please Enter the Email Address")]
        //[EmailAddress(ErrorMessage = "Please Enter a Valid Email")]
        [RegularExpression(@"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
            ErrorMessage = "Please Enter a Valid Email")]
        public string Email { get; set; }
        [Required(ErrorMessage = "Please Select the Department")]
        public Department? Department { get; set; }
    }
}

With the above changes in place, run the application, navigate to the /Employee/Create URL, and submit the form by entering an invalid email address. You should get the appropriate error message, as shown in the below image.

Using Multiple Model Validation Attributes

Currently, Validation is performed on the server side, not on the client side. The Validation attributes in ASP.NET Core MVC Framework provide both client-side and server-side validation. Let us proceed to understand how to enable client-side validation in ASP.NET Core MVC Application.

Enabling Client-Side Validation in ASP.NET Core MVC:

Client-Side Validation in ASP.NET Core MVC Application is a feature that allows us to perform validation on the client browser without making a round trip to the server. This provides a more responsive experience for the user. ASP.NET Core MVC uses jQuery Validation and jQuery Unobtrusive Validation for client-side validation. Here’s how you can enable client-side validation in an ASP.NET Core MVC application:

You must include jQuery, jQuery Validation, and jQuery Unobtrusive Validation in your project. These can be added via CDNs or local files. For example, include them via CDNs in your _Layout.cshtml:

<!-- jQuery --> 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<!-- jQuery Validation Plugin --> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.5/jquery.validate.min.js"></script>
!-- jQuery Unobtrusive Validation (to bridge ASP.NET Core MVC validation with jQuery Validate) --> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.13/jquery.validate.unobtrusive.min.js">

Please add the following files in your _Layout.cshtml to enable client-side validation using local files. The order is also important, and please include them in the following order.

<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

The order in which the script files are referenced is also important. This is because jquery.validate is dependent on jquery, and jquery.validate.unobtrusive is dependent on jquery.validate. So, they should be referenced in the above order. Otherwise, client-side validation will not work as expected. In short, JavaScript is parsed “top-down”, so all dependencies must be referenced before the dependent reference.

With these changes in place, the client-side validation should be active in your ASP.NET Core MVC application. Whenever a user attempts to submit the form, jQuery Validation will check input values against the data annotations before the form is submitted. If validation fails, appropriate error messages will be displayed next to the form fields. 

ASP.NET Core MVC uses these libraries to enhance and streamline client-side scripting and validation:

  • jQuery: It provides core functionality for DOM manipulation and event handling, making it simpler to write complex client-side scripts that enhance user experience.
  • jQuery Validation: Integrated with ASP.NET Core MVC’s server-side validation, this plugin ensures that validation rules are enforced on the client side before the form data is sent to the server. This reduces the server load and provides immediate feedback to users.
  • jQuery Unobtrusive Validation: Works with the jQuery Validation plugin and ASP.NET Core MVC’s validation annotations. It uses data-* attributes generated from model annotations to control the behavior of client-side validation without additional scripting. This helps keep your JavaScript code minimal and separated from HTML content.
Best Practices to Use Validation Related JavaScript files:

We should not include the following three JavaScript files in our Layout File to enable Client-Side Validation using Data Annotation.

<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

You need to remember that we are not going to create forms or perform client-side validations on every web page of our application. If we include the above JavaScript files inside our Layout files, they will be downloaded on every page where we use the layout file, even if we are not performing client-side validations.

Creating a Partial View:

So, to overcome this problem, let us create a partial view with the name _ValidationScriptsPartial.cshtml within our application’s Views/Shared folder and then copy and paste the following code into it. We are not including the jquery.js file because the jQuery.js file is required in all the pages, and hence, it is better to put that file inside the layout file.

<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

Next, we must add a render section inside the layout file to include the partial view above. So, add the following code just before the end of the body tag of the layout file.

@await RenderSectionAsync("Scripts", required: false)

This is where we need to add the validation script. Suppose our Create.cshtml view wants client-side validation; then, we need to add the following code just before the end of the view.

@section Scripts {
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

With these changes in place, run the application, and you will see that the client-side validation is working as expected.

Using HTML Helper to Provide Validation Message:

Instead of using asp-validation-summary and asp-validation-for tag helpers, we can also use Html.ValidationSummary and Html.ValidationMessageFor html helper methods to achieve the same. So, modify the Create.cshtml file as shown below to use HTML helper methods to display the error message.

@model DataAnnotationsDemo.Models.Employee

@{
    ViewData["Title"] = "Create";
}

<h1>Create Employee</h1>

<div class="row">
    <form asp-controller="Employee" asp-action="Create" method="post" class="mt-3">
        @*<div asp-validation-summary="All" class="text-danger"></div>*@
        <div class="text-danger">
            @Html.ValidationSummary()
        </div>
        <div class="form-group row">
            <label asp-for="Name" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <input asp-for="Name" class="form-control" placeholder="Enter Your Name">
               @* <span asp-validation-for="Name" class="text-danger"></span>*@
                <span class="text-danger">
                    @Html.ValidationMessageFor(x => x.Name)
                </span>
            </div>
        </div>
        <div class="form-group row">
            <label asp-for="Email" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <input asp-for="Email" class="form-control" placeholder="Enter Your Email">
               @* <span asp-validation-for="Email" class="text-danger"></span>*@
                <span class="text-danger">
                    @Html.ValidationMessageFor(x => x.Email)
                </span>
            </div>
        </div>

        <div class="form-group row">
            <label asp-for="Department" class="col-sm-2 col-form-label"></label>
            <div class="col-sm-10">
                <select asp-for="Department" class="form-select" 
                        asp-items="Html.GetEnumSelectList<Department>()">
                    <option value="">Please Select</option>
                </select>
                @*<span asp-validation-for="Department" class="text-danger"></span>*@
                <span class="text-danger">
                    @Html.ValidationMessageFor(x => x.Department)
                </span>
            </div>
        </div>

        <div class="form-group row">
            <div class="col-sm-10">
                <button type="submit" class="btn btn-primary">Create</button>
            </div>
        </div>
    </form>
</div>

@section Scripts {
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}
Which Data Annotation Attributes support Client Side Validations in ASP.NET Core MVC?

All Data Annotation Attributes are not supported by client-side validation. The following are the data annotation attributes that support client-side validation in ASP.NET Core MVC:

  • [Required]
  • [StringLength(maxLength)]
  • [Range(minimum, maximum)]
  • [Compare(otherPropertyName)]
  • [RegularExpression(pattern)]
  • [EmailAddress]
  • [Phone]
  • [Url]
  • [CreditCard]
Best Practices to Implement Validation in a Website:
  • Combine with Client-side Validation: Use server-side and client-side validation for better security and user experience.
  • Consistent Validation Across Client and Server: Ensure that client-side and server-side validations follow the same rules for consistency and redundancy.
  • User Feedback: Provide clear, helpful error messages for the end users when their inputs fail server-side validation.

In the next article, I will discuss How to Perform Model Validations using Data Annotation Attributes in ASP.NET Core MVC Application with Examples. In this article, I try to explain Model Validations in ASP.NET Core MVC with Examples. I hope you enjoy this Model Validations in ASP.NET Core MVC article.

Leave a Reply

Your email address will not be published. Required fields are marked *