Published: 11 May 2011
By: Bipin Joshi
Download Sample Code

Learn to use various validation techniques of ASP.NET MVC to enforce validation rules on the user input.

Contents [hide]

Introduction

Validating data entered by the user is one of the most common tasks in web applications. As far as ASP.NET MVC is concerned there are various ways to accomplish this task. As a web developer you should be aware of these options so that depending on your requirement you need to pick the appropriate one. This article discusses many of the available options and also shows in step-by-step manner how these options can be put to use in your ASP.NET MVC application.

NOTE

Throughout this article you will be using ASP.NET MVC 3 and ASPX views.

Available Options

In the remainder of this article you will learn about the following validation options:

  • Adding validation errors directly to ModelState: This is a bit raw technique wherein you perform validations in the controller and add validation errors to the ModelState object.
  • Validating data using Data Annotation Attributes: In this approach you use data annotation attributes to decorate data model properties. The data annotation attributes in turn take care of validations and validation errors (if any) are added to ModelState object for you.
  • Validating data using IDataErrorInfo Interface: In this technique your data model class implements IDataErrorInfo interface and implements a property and an indexer. The implementation performs the validation tasks.
  • Validating data using IValidatableObject Interface: In this case your data model class implements IValidatableObject interface and provides logic for Validate() method.
  • Client side Validation: All the above techniques are server side techniques. As the name suggests, this technique uses client side script for validation purpose.
  • Remote Validation: In this approach you call server side code to perform validation without a full post back to the server.

Sample Database

Throughout the remainder of this article you will use a SQL Server database with one table – Users. The Users table is supposed to store some information related to website users and has schema as shown in Figure 1.

Figure 1: Schema of Users table

Schema of Users table

The UserId column acts as the primary key and is marked as an identity column. The other column names are self explanatory. In order to add new users to the database you will create a view that simply grabs all the values from the end user. The data collected from the end user will be validated and then put in the Users table.

Controller and Views

You need to add a controller – UserController – that takes care of GET and POST requests and renders views accordingly. The UserController essentially consists of the Index() action methods as shown below:

The first Index() method takes care of GET requests and remains the same in all the further examples. The second Index() method is intended to take care of POST requests. This Index() action method will take different parameters and will have different logic depending on the validation technique under consideration.

Now that you have added the controller, let's add the views. You need two views for our examples and testing – Index and Success. The Index view essentially renders a data entry form wherein you can enter values for various data fields. The Index view displays validation errors (if any) and is used in Index() action methods (GET and POST). The HTML markup of the Index view is shown below (unwanted markup has been omitted for clarity):

As you can see, several HTML helpers are used in the markup above to render a FORM. Make sure to include all form fields inside the "using" block. The helper methods such as LabelFor(), TextBoxFor(), TextAreaFor(), PasswordFor() and DropDownListFor() render the corresponding HTML form elements for the specified data model property. Notice that the Index view is a strongly typed view and uses User class as its data model. Also notice how ValidationMessageFor() helper methods are used. The ValidationMessageFor() method returns a validation error (* in the above markup) in case the data model property under consideration has invalid value. The ValidationSummary() helper renders a list of validation error messages from the form.

Figure 2 shows the Index view as rendered in the browser.

Figure 2: Index view at run time

Index view at run time

The Success view is called if user information is validated successfully (Figure 3) and simply contains markup as shown below:

Figure 3: Success view at run time

Success view at run time

Adding validation errors directly to ModelState

In the first example, you will use ModelState object to flag any validation errors. To use the ModelState object go to the second Index() action method and modify it as shown below:

Here, you changed the Index() action method to accept a FormCollection parameter. This way values posted from the index view will be received in the form of a collection. You can then individually check the values of the submitted form fields. If any validation rule is violated you add a validation error in the ModelState using AddModelError() method. The following code shows how this is done:

The first validation rule checks if the length of FirstName is between 3 and 50. If the rule is violated the code adds a validation error using AddModelError() method. The first parameter of the AddModelError() method is a key and the second parameter is the error message to be displayed. Make sure to specify the key name same as associated form field name. Doing so will ensure that the validation errors are displayed at field level as well as at summary level. Calling AddModelError() method actually populates the Errors collection of the ModelState (see Figure 4). Each error you add creates a ModelError instance with its ErrorMessage property set to the validation error message you specify.

Figure 4: Adding validation errors to ModelState

Adding validation errors to ModelState

Once your validation checks are over you need to decide whether to add the user data in the database or display error messages. You do this at the end of the Index() action method like this:

The IsValid property of the ModelState object tells you whether there were any validation errors or not. If the IsValid property returns true you can proceed with the data inserting (not shown in the above code) and render the Success view. Otherwise you render the Index view again. Since the ModelState contains validation errors the Index view will display them at field level and at summary level (Figure 5).

Figure 5: Index view with validation errors

Index view with validation errors

Note that the look and feel of the field level validations and validation summary is controlled by certain CSS classes that are present in the default Style Sheet of the application (see "Styles for validation helpers" section of the Site.css). You can customize the look and feel by modifying these CSS classes.

In the above example you used FormCollection as an action method parameter and it worked fine as far as our validations are concerned. However, it is a bit raw technique of receiving data and wherever possible you should use strongly typed approach (a data model class for example).

Validating data using Data Annotation Attributes

The System.ComponentModel.DataAnnotations namespaces contains several attribute classes that provide inbuilt validation capabilities for common scenarios. These attributes are used to decorate properties of the data model. For example, if you wish to ensure that FirstName property must be assigned some value then you can simply decorate the FirstName property with [Required] data annotation attribute. Other frequently used data annotation attributes include [StringLength], [Range] and [RegularExpression]. Though detailed discussion of these attributes is beyond the scope of this article, you will make use of some of these attributes to validate the user data.

In order to use data annotation attributes you first need to create a data model class. It can be a normal .NET class, LINQ to SQL class or an Entity. In this example you will create a LINQ to SQL class. Begin by adding a new LINQ to SQL class in the Models folder of the application. With the .dbml file opened in Visual Studio, drag and drop the Users table on its designer surface. This will create a LINQ to SQL class – User (see Figure 6).

Figure 6: LINQ to SQL class for Users table

LINQ to SQL class for Users table

At first you may think of applying the data annotation attributes to the User class by opening and modifying the generated code. However, this is not a recommended approach because the LINQ to SQL classes may get regenerated during the course of development. For example, say you changed the schema of the underlying table, then deleted the User class and then again created it so as to reflect the new changes. In this process your previously added data annotations may get lost and you will need to add them again. The better approach, therefore, is not to touch the auto-generated code; rather add a metadata class that specifies the data annotations. Let's see how.

Add a new class in the Models folder and name it as UserMetadata. The completed UserMetadata class is shown below:

As you can see, the UserMetadata class defines properties matching with the User class. You need to create only those properties for which you want to apply the data annotation attributes. Once created you can apply data annotation attributes. For example, the [StringLength] attribute applied to the FirstName property specifies the maximum length (50), minimum length (3) and an error message to be displayed in case of validation error. The [DisplayName] attribute governs how the property names should be displayed in the error messages. For example, your property name is Email but in error messages displayed to the user you want it to be Email Address.

The UserMetadata class is currently not related to the User class in any way. To associate it with the user class you need to create a partial class and decorate it with [MetadataType] attribute. The following fragment shows how this is done.

The partial class User is decorated with MetadataType attribute that specifies UserMetadata as the class containing metadata information.

Ok. Now open the UserController again and change the POST Index() action method like this (you may comment the existing version of Index() action for later reference):

The Index() action method now accepts a parameter of type User. When you submit the form, the MVC default model binder will map the form fields with the data model properties (remember that our form field names and data model property names are the same). In the process, MVC framework will also perform validations on the various pieces of data as per data annotation attributes. The validation errors (if any) will also be populated in the ModelState. So, you can directly check the IsValid property to determine whether there are any validation errors or not. If there are no validation errors you simply add a user to the database using the data context; otherwise Index view is displayed along with error messages. Figure 7 shows a sample run of the application with validation errors.

Figure 7: Data annotation attributes in action

Data annotation attributes in action

Validating data using IDataErrorInfo Interface

At times data annotation attributes are inadequate because of the complexity of the validation logic and you need some flexible yet simple way. For example, your validation logic may involve database access or talking to some other system. The IDataErrorInfo interface can provide such a way of validating data. The IDataErrorInfo interface resides in System.Component namespace and expects you to implement two members – indexer and Error property. To use IDataErrorInfo interface remove the [MetadataType] attribute placed on the User partial class for the time being and modify the User partial class as shown below:

The User class now implements IDataErrorInfo interface. The indexer allows you to validate each and every property value. A switch-case statement does that for FirstName, LastName, Email and Notes properties. The Error read-only property on the other hand is intended to flag an error related to the object as a whole (though in the above code both contain similar logic). Figure 8 shows how validation errors are displayed when validation for FirstName and LastName properties fails.

Figure 8: Validating data using IDataErrorInfo

Validating data using IDataErrorInfo

Validating data using IValidatableObject Interface

The IDataErrorInfo interface is available since early days of .NET framework. Along with data annotations another interface got added to help developers validate their objects – IValidatableObject interface. The IValidatableObject interface is intended to validate an object as a whole. In order to use the IValidatableObject interface you need to write the implementation for Validate() method. The Validate() method returns a collection of ValidationResult instances. The ValidationResult class stores the outcome of a validation operation. The following code shows how IValidatableObject interface can be implemented.

The Validate() method returns a collection of validation errors (if any) with the help of "yield" keyword. Notice how the ValidationResult instances are created. The first parameter is the error message and the second parameter is the member that has the error. Figure 9 shows a sample run of the Index view with validation errors.

Figure 9: IValidatableObject interface in action

IValidatableObject interface in action

Client side Validation

In all the examples so far the actual validation logic was being executed on the server after a form postback. This causes an extra round trip to the server. Luckily, ASP.NET MVC also supports client side validations. The client side validation support and data annotation attributes go hand in hand in that ASP.NET MVC automatically generates the client side validation logic based on the data annotation attributes you use. This way there is no need to manually write any client side script. The client side validation support comes in two flavors – AJAX based and jQuery based. The former flavor essentially comes from MVC 2 whereas the later one comes with MVC 3. Though we are going to see both of them in action, unless there is any specific reason you should stick to jQuery based validation. The jQuery based validation is often termed as "unobstructive" validation because instead of emitting client side JavaScript (as done by AJAX based validation mechanism) it makes use of special HTML 5 attributes.

Before you go ahead with examples in this section make sure to enable the data annotation attributes as discussed earlier in section titled "Validating data using Data Annotation Attributes". The next step is to enable the client side validation. You can enable the client side validation from three places – web.config, Global.asax and individual view. Let's see all the three ways.

In order to enable the client side validation from web.config you need to add the following keys in the <appSettings> section:

The ClientValidationEnabled key is used to enable/disable client side validation whereas the UnobtrusiveJavaScriptEnabled key is used to enable/disable unobstructive validation mode. The same task can be accomplished via Global.asax as follows:

The ClientValidationEnabled and UnobtrusiveJavaScriptEnabled properties of the HtmlHelper class allow you to turn the respective features on and off. Finally, you can enable them at view level like this:

The EnableClientValidation() and EnableUnobtrusiveJavaScript() methods of the Html object allow you to turn the corresponding features on and off.

Once you enable the client side validation, next step is to include certain client script files in your views. These client script files contain the client side code necessary to validate your data. These files are available in the Scripts folder of the default web site. You can also refer them from Microsoft Ajax CDN. Assuming you are referring local versions of the client script files the necessary files to be referred are as follows:

As you can see the first set of files is for AJAX based client validations whereas the second set of files is for jQuery based unobtrusive validations (you need to include only one of the set depending on the chosen mode of validation). Notice the use of Url helper class. The Content() method converts a virtual path in to an absolute path.

After adding the script references in the Index view run the application and try to submit some invalid values. You will observe that this time without any postback the page shows the validation errors as defined by the data annotation attributes. If you see HTML source of the view as rendered in the browser you will see something similar to Figure 10 (for AJAX based validations) and Figure 11 (for jQuery based unobtrusive validations).

Figure 10: JavaScript emitted by the AJAX validation scheme

JavaScript emitted by the AJAX validation scheme

Figure 11: Special HTML 5 attributes (data-*) emitted by unobstructive validation scheme

Special HTML 5 attributes (data-*) emitted by unobstructive validation scheme

Remote Validation

At times you need to perform some validations on the data being entered that are based on some server side resource. Say, for example, you want to enforce that there are no duplicate emails in the Users table. The client side validation schemes will not have any idea about the validation logic you wish to execute in this case. You can, of course, use server side validation but it will have the penalty of extra round trip. In such cases you can make use of remote validation. In case of a remote validation, the actual validation code resides on the server and gets invoked from the client side without need of a full postback. You perform remote validations with the help of [Remote] attribute. When you use the [Remote] attribute ASP.NET MVC makes use of jQuery to perform the validation. Here is how you use the [Remote] attribute:

As seen the above piece of code the [Remote] attribute takes two parameters. The first parameter indicates the name of a server side method that performs the validation (CheckDuplicateEmail in this case). The second parameter indicates the name of controller that supplies the validation method. The CheckDuplicateEmail() validation method is shown below:

The CheckDuplicateEmail() accepts a string and returns a JsonResult. Inside, it checks whether the supplied email already exists in the database using a LINQ query. If a duplicate is not found it returns true in JSON format; otherwise it returns an error message in JSON format.

If you now run the application and try to enter some duplicate email address, you will get an error message like this:

Figure 12: Remote validation in action

Remote validation in action

Summary

This article showed various validation techniques that can be used in MVC 3. We discussed the following techniques:

  • Adding validation errors directly to ModelState:
  • Validating data using Data Annotation Attributes
  • Validating data using IDataErrorInfo Interface
  • Validating data using IValidatableObject Interface
  • Client side Validation
  • Remote Validation

Adding validation errors directly to ModelState is a simple way of performing validations that are not bundled with the data model. This technique is also useful when there is no direct mapping between form data and the data model.

The data annotation attributes provide a handy and quick way of performing common validations. More importantly ASP.NET MVC is also capable to emitting client side validation logic based on these attributes. Chances are that you will find them using often in any MVC application you build.

The IDataErrorInfo and IValidatableObject interfaces allow you to plug-in a custom validation logic that is difficult or impossible to execute using the previous techniques.

Finally, remote validations are useful in situations where some server side resource is involved but round trip is to be avoided.

Depending on your requirement you can use one or more of the above technique to validate your form data.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Bipin Joshi

Bipin Joshi is a blogger, author and a Kundalini Yogi who writes about apparently unrelated topics - Yoga & Technology! A former Software Consultant and trainer by profession, Bipin is programming since 1995 and is working with .NET framework ever since its inception. He is an internation...

This author has published 7 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


Code First Approach using Entity Framework 4.1, Inversion of Control, Unity Framework, Repository and Unit of Work Patterns, and MVC3 Razor View
A detailed introduction about the code first approach using Entity Framework 4.1, Inversion of Contr...
jQuery Mobile ListView
In this article, we're going to look at what JQuery Mobile uses to represent lists, and how capable ...
Exception Handling and .Net (A practical approach)
Error Handling has always been crucial for an application in a number of ways. It may affect the exe...
JQuery Mobile Widgets Overview
An overview of widgets in jQuery Mobile.
jQuery Mobile Pages
Brian Mains explains how to create pages with the jQuery Mobile framework.

You might also be interested in the following related blog posts


Introducing Versatile DataSources read more
How to display data from different tables using one data source read more
Get Ready for Teleriks Custom-built Extensions for ASP.NET MVC read more
What needed to be monitored to get better Governance read more
ASP.NET 4 Beta 2 - New Version, New Docs, New MSDN Site ! read more
Announcing Microsoft Ajax Library (Preview 6) and the Microsoft Ajax Minifier read more
Health Monitoring and ASP.NET MVC read more
Why not Classic (Legacy) ASP? read more
MvcContrib working on Portable Areas read more
RIA Svcs Data Source Control read more
Top
 
 
 

Please login to rate or to leave a comment.

Free Agile Project Management Tool from Telerik
TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.