Customizing Validation Attributes in MVC
Disclaimer: This is an old post, moved as is (with minor formatting change) from my old blog.
Background
As many of you already know, MVC provides validation attribute functionality to validate view models. A few very commonly used validation attributes are: Required
, RegularExpression
and Range
. Below is one simple example:
Which will give you the following output:
This default behavior is good for less complex application, where you can apply factory made validation without any considerable effort from your side.
You can also enable client side validation (validation will be done at client side without server post back) by adding/modifying the following lines in the
-
Web.config
file:
- Layout / View page (of course you can use the latest version of jQuery):
So far so good, Now lets complicate it
Lets assume that this application is going to be used by people from different regions and every region has a specific format for phone number. Every region has a separate set of special characters to be allowed for the phone number.
For this you need to change the regular expression of the RegularExpression
attribute for the phone number property on runtime. Now by design you can’t modify the attribute property value on runtime.
For example I’ve the below view model. Now I want to change the regular expression of RegularExpresssionAttribute
or the max range of RangeAttribute
, only for one instance of my ViewModel
class, inside my controller action and pass the model instance to view so that the view is rendered with new validation attributes.
I can’t assign the attribute explicitly at runtime as Attribute is read only.
If tried above it’ll give compilation error:
Property or indexer
System.ComponentModel.AttributeCollection.this[System.Type]
cannot be assigned to – it is read only.
If you’ve worked in ASP.NET form based application, you’ll understand the pain, where this same is possible by setting a few properties of different validator controls.
So how to tame this behavior?
It’s a good thing that MVC also allows you to build your own custom validation attribute, where you can manufacture validation attribute depending upon your need. Making your own custom validation attribute is a two step process.
-
Server side validation: you need to inherit
ValidationAttribute
(namespace:System.ComponentModel.DataAnnotations
) abstract class. -
Client side validation: you need to implement
IClientValidatable
(namespace:System.Web.Mvc
) interface.
In this example we’ll look for other property for the validation criteria for the target property of the target model class.
We’ll go step by step.
First create a MVC 4 Web Application project using basic template. I’ve named it MVCCustomValidation
.
Next create a class in Models folder name TargetModel.cs
Next add a new folder called Infra
in the root directory of our MVCCustomValidation
project by right clicking project and then selecting Add—>New Folder.
Inside this new folder add a class called REAttribute.cs
and modify the content as below:
We’ve created a sealed attribute class REAttribute
which inherits ValidationAttribute
class. Constructor for this validation attribute class takes only one parameter which is the name of the property that provides regular expression for the target property(REForTargetProp
in our case).
We created our own overridden version of IsValid()
method. This method is called to check whether the value of the target property is valid or not. This method takes two parameters:
-
value
is the value to be validated, i.e. the value of the target property, and -
ValidationContext
object which contains information about the validation request.
We’ve used the ValidationContext
object to extract the value of regular expression provider property. We’ve used regex pattern matching to validate the value
against the regular expression extracted and returned validation result accordingly.
Now the server side validation setup is done. We need to apply this attribute in our TargetModel
class.
Now create a controller named HomeController
and modify the content as below:
The controller code is pretty simple. The first Index
method calls a view with blank instance of TargetModel
object only passing the regular expression. The second Index
method (one with HttpPost
selector) validates the model data on postback. If it is not valid, same view page is returned to the user or else user is redirected to Success
page.
Note that we’ve passed regular expression to validate numeric digits.
In order to create view, right click any method name in the controller and select Add View
option. Create two strongly typed views named Index.cshtml
and Success.cshtml
. While creating the views also select the option to use layout or master page.
Make sure the content of the Index.cshtml
and Sucess.cshtml
matches the below content:
- Index.cshtml:
- Success.cshtml:
Now run the application. You can test the custom validation attribute by entering invalid values:
If you notice, the page is making a post back to check the values. Because we’ve not yet applied client side validation.
Applying client side validation is a two step process,
- As told earlier you need to implement
IClientValidatable
interface in your custom validation attribute. - You need to write your own client side script.
Implementing IClientValidatable
interface:
The newly added code is marked in bold. By implementing GetClientValidationRules
method we’re providing support for client validation. This method returns ModelClientValidationRule
objects which is used by framework to output HTML 5 data-xxx
attributes for the target property, which are to be used to perform client side validation. Hence we must pass all the necessary attributes to client side which are required for client side validation.
If you run the application now you can see these data-xxx
attributes added in the HTML response sent to the browser.
Writing your own jQuery Script:
Now the next step is to set up your own Client Side script to use these metadata values and provide client side validation.
In the Scripts folder we’ve added a new folder called CustomScripts
and inside this folder we’ve added a new javascript file called REScript.js
. Modify the content so that it matches below code:
In the first step we’re adding an validation adapter:
-
adaptername
must match theValidationType
value that is passed from server side code, i.e. it must matchXXX
part ofdata-val-XXX
whereXXX
is the name of the validation type. - params is the paramters passed from server side and rendered as HTML 5 attribute:
data-val-XXX-NNN
whereXXX
is the name of the validation type andNNN
is the name of the parameters. - function is called to convert HTML 5 data attributes to jQuery equivalent parameters, so that jQuery validate function can use those.
You can get a more detailed description on adapters in Unobtrusive Client Validation in ASP.NET MVC 3 by Brad Wilson.
Now the second step is where you write your actual jQuery method by using $.validator.addMethod()
. Here we’re extracting the regular expression using the parameter passed(here reproviderproperty
) and testing the value passed against this regular expression. This is fairly simple.
You need to include this custom script of your own in the view page and also need to include jQuery libraries in the layout page.
_Layout.cshtml
Index.cshtml
The last step is to check whether your web.config has the below settings or not:
And you are ready to go. Test your application in browser, enter a non-numeric value in the text box and just move the focus from it without clicking the submit button and see the result.
You can also get the full code from here.
Note: This post is entirely based on my personal RND. If anyone finds something wrong in the code shared or a better approach to achieve the same result, kindly don’t hesitate to share.
Hope this helps.