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.
Throughout this article you will be using ASP.NET MVC 3 and ASPX views.
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.
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
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:
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
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
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
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
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:
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
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
[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
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):
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
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
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
LastName properties fails.
Figure 8: 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.
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
Client side Validation
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
ClientValidationEnabled key is used to enable/disable client side validation whereas the
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 11: Special HTML 5 attributes (data-*) emitted by unobstructive validation scheme
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
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:
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
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.
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.
Please login to rate or to leave a comment.