Validation In Razor Web Pages 2

The new release of ASP.NET Web Pages - version 2 - doesn't include many obvious changes, but the most significant one is an enhanced Validation system. A couple of new classes have been introduced, and Web Pages validation now works with the MVC Unobtrusive jQuery validation library. This article explores the new validation system and sees what it brings to the party.

To begin with, it's worth noting that Web Pages v 1.0 style validation still works as it always did. You can still use ModelState to log validation errors and test to see if ModelState.IsValid before processing any user input. Version 2.0 introduces two new classes: ValidationHelper and Validator. The Validator class offers a number of different types of validator:

  • Validator.DateTime()
  • Validator.Decimal()
  • Validator.EqualsTo()
  • Validator.Float()
  • Validator.Integer()
  • Validator.Range()
  • Validator.Regex()
  • Validator.Required()
  • Validator.StringLength()
  • Validator.Url()

These methods result in differing validation being applied to values. The DateTime(), Decimal(), Float(), Integer() and Uri() methods all test for data type. The EqualsTo() method enables you to test to see if the value provided in one field is the same as the value provided in another. This is used commonly when you ask someone to enter their password twice on registration to ensure that they got it right first time. The Range() method tests to see if a value is within a specified range of numbers. The Regex() method takes a regular expression pattern and test to see if the supplied value matches the pattern. You might use this to test if a value is in a valid format for an email address, for example. The Required() method is a nice replacement to the old IsEmpty() test, in that it dictates that the field it is applied to is mandatory. Finally, the StringLength() method enables you to specify a minimum and/or maximum number of characters that will be accepted for any value submitted.

The Validation helper class offers some key methods and properties including the following:

  • Validation.Add()
  • Validation.ClassFor()
  • Validation.For()
  • Validation.IsValid()
  • Validation.RequireField()
  • Validation.RequireFields()

There are two ways of specifying that a form field is mandatory. One is to use the Validation.RequireField() method, and the other is to use the Validation.Add() method:

Validation.RequireField("firstname", "You must provide a first name");
Validation.Add("firstname", Validator.Required("You must provide a first name"));

The RequireField method takes the name of the form field and an optional error message. If you do not supply your own error message, the default one provided by the Web Pages framework is used instead. For required fields, that default error message is "This field is required". The Add method is more wordy in that you must specify the type of validator you want to apply through the Validator class. However, it is more flexible in that you can specify any number of different types of validator:

Validation.Add("firstname", 
    Validator.Required("You must provide a first name"), 
    Validator.StringLength(10, 0, "No more than 10 letters")
);

The RequireFields method provides a means for you to specify multiple fields as mandatory, but you cannot provide tailored error messages for each one. You have to reply on the default error message. Lazy, but if you are developing an Intranet where appearances are not important, you might use this:

Validation.RequireFields("firstname", "lastname", "email");

Here's a sample form that shows a variety of validators applied, and the use of the Validation.IsValid() method to test that all validators passed:

@{
    var result = "";
    Validation.Add("firstname", 
        Validator.Required("You must provide a first name"), 
        Validator.StringLength(10, 0, "No more than 10 letters")
        );
    Validation.RequireField("lastname", "Gimme your Last name!");
    Validation.Add("birthdate",
        Validator.Required(),
        Validator.DateTime()
        );
    Validation.Add("webaddress", 
        Validator.Required("URL is required"),
        Validator.Url("Must be a valid web address")
        );
    Validation.Add("email", 
        Validator.Regex(@"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$", 
        "Invalid format for an email address")
        );    
    Validation.Add("number", 
        Validator.Required("Make sure you say how many you want"),
        Validator.Range(1,4, "Must be between 1 and 4")
        );
    Validation.RequireField("password", "Password cannot be empty");
    Validation.Add("password2", 
        Validator.Required("Put your password in here again"),
        Validator.EqualsTo("password", "Must be the same as your password")
        );
    if(IsPost){
        if (Validation.IsValid()) {
            result += "<p>You entered:</p>";
            foreach(string item in Request.Form){
                result += item + ": " + Request[item] + "<br />";
            }
        }
        else{
            ModelState.AddFormError("There are some errors with your submission"); 
        }
    }
}

<!DOCTYPE html>
<html lang="en">
    <head>

        <link href="~/Content/StyleSheet.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <fieldset>
            <legend>Web Pages 2 Validation</legend>
            <form method="post" action="">
                <div class="row">
                    @Html.Raw(result)
                    @Html.ValidationSummary(true)
                </div>
                <div class="row">
                    <label class="label" for="firstname">First Name:</label>
                    <span><input name="firstname" type="text" value="@Request["firstname"]" /></span>
                    @Html.ValidationMessage("firstname")
                </div>
                <div class="row">
                    <label class="label" for="lastname">Last Name:</label>
                    <span>@Html.TextBox("lastname", Request["lastname"])</span>
                    @Html.ValidationMessage("lastname")
                </div>
                <div class="row">
                    <label class="label" for="birthdate">Birth Date:</label>
                    <span>@Html.TextBox("birthdate", Request["birthdate"])</span>
                    @Html.ValidationMessage("birthdate")
                </div>
                <div class="row">
                    <label class="label" for="webaddress">Web Address:</label>
                    <span>@Html.TextBox("webaddress", Request["webaddress"])</span>
                    @Html.ValidationMessage("webaddress")
                </div>
                <div class="row">
                    <label class="label" for="email">Email:</label>
                    <span>@Html.TextBox("email", Request["email"])</span>
                    @Html.ValidationMessage("email")
                </div>
                <div class="row">
                    <label class="label" for="number">Number Required:</label>
                    <span>@Html.TextBox("number", Request["number"])</span>
                    @Html.ValidationMessage("number")
                </div>
                <div class="row">
                    <label class="label" for="password">Password:</label>
                    <span>@Html.Password("password")</span>
                    @Html.ValidationMessage("password")
                </div>
                <div class="row">
                    <label class="label" for="password2">Password Again:</label>
                    <span>@Html.Password("password2")</span>
                    @Html.ValidationMessage("password2")
                </div>
                <div>
                    <span class="label">&nbsp;</span>
                    <span><input type="submit" value="Submit" /></span>
                </div>
            </form>
        </fieldset>
    </body>
</html>

The Html.ValidationSummary() method from Web Pages v 1 is still available and will display general messages that have been applied through the ModelState.AddFormError method, and optionally, all individual field validation error messages if the excludeFieldErrors parameter is set to "false" (as is not the case above). You can see that, along with some other interesting things in the image below, which is the result of submitting the form to the server without entering any values at all:

Individual field validation error messages are rendered using the Html.ValidationMessage() helper, which takes the name of the field to be validated as a parameter. This hasn't changed from v 1. Notice in this example that some of the inputs have a pink background, whereas some others don't. Here is the part of the style sheet that controls the appearance of the inputs:

.validation-summary-errors {
    border: 2px solid #990099;
    color: red;
}

.field-validation-error {
    color: #990099;
}

.input-validation-error {
    color: #990099;
    background-color: #ff80ff;
    border-top: 2px solid #990099;
    border-left: 2px solid #990099;
}

The validation-summary-errors rule is applied to the Html.ValidationSummary output. The field-validation-error style is applied to the individual field error messages, and the input-validation-error class is responsible for styling the actual form inputs when they are associated with a failed validation. However, the firstname field is not styled, even though it clearly failed validation, whereas all others have been styled as a result of failed validation. The reason for this is that when you use Html Helpers to render your form fields, the CSS class is applied automagically by the Web Pages framework in the result of failed validation. All fields in the above code except the firstname field are Html helpers. If you want the input-validation-error style to be applied to normal HTML input elements, you need to state that explicitly with the Validation.ClassFor() method:

<input name="firstname" type="text" value="@Request["firstname"]" class="@Validation.ClassFor("firstname")" />
Unobtrusive Client Validation

So far, the example has shown server-side validation in action. And the new validation framework will always ensure that server-side validation takes place. However, as a convenience to your users, you may want to apply client-side validation, which runs in the browser and can give users instant feedback as they move through the form if they attempt to provide invalid values, or if fields are empty when they click the submit button. Since MVC 3, the ASP.NET MVC framework has included a mechnism called Unobtrusive Client Validation. This is a kind of bridge between server-side validators - applied using DataAnnotation attributes on model properties, and the popular jQuery Validate plugin. The same system has now been applied to the Web Pages framework, acting as a bridge between the server-side validators you apply through the Validation.Add or the Validation.RequireField(s) methods, and the jQuery Validate library. In order to get it to work with the form in the example, you need to do two things: first add references to jQuery, jQuery.Validate and jQuery.Validate.Unobtrusive:

    <head>
        <title>Web Pages 2 Validation</title>
        <script src="~/Scripts/jquery-1.7.2.min.js" type="text/javascript"></script>
        <script src="~/Scripts/jquery.validate.min.js" type="text/javascript"></script>
        <script src="~/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
        <link href="~/Content/StyleSheet.css" rel="stylesheet" type="text/css" />
    </head>

You can link to these files through Microsoft's AJAX CDN if you like: http://www.asp.net/ajaxlibrary/cdn.ashx#ASPNET_MVC_Releases_on_the_CDN_11 or get them in the download that accompanies this article. The second thing you need to do - but only where you are not using Html Helpers for form fields - is to explicitly tell jQuery to apply client-side validation through the Validation.For method:

<input name="firstname" type="text" value="@Request["firstname"]" class="@Validation.ClassFor("firstname")" @Validation.For("firstname") />

As with the Validation.ClassFor method, this is not needed when using Html Helpers. When you implement unobtrusive validation, additional attributes are generated by the framework based on the HTML5 data-* attribute. For example, here's the source for the Number Required field:

<input data-val="true" 
       data-val-range="Must be between 1 and 4" 
       data-val-range-max="4" 
	   data-val-range-min="1" 
       data-val-required="Make sure you say how many you want" 
       id="number" 
       name="number" 
       type="text" 
       value="" />

I've broken it over several lines so that it easier to see and to ensure it fits on your screen. The data-val attribute is set to true, and tells jQuery that this field should be validated on the client. The other data-val attributes are pretty self explanatory and ensure that the right type of validation is performed, and that the correct error messages are assoicated with the individual form field. It should be noted at this point that certain types of validation are not available on the client by default. These relate to data type validation: Validator.DateTime(), Validator.Decimal(), Validator.Float(), Validator.Integer(), Validator.Url(). However, these validators are checked on the server.

Validation has certainly taken a step forward in Web Pages 2, and the addition of the Validation helper and Validator class means that it is really easy to ensure that you only get acceptable data posted to your application. In addition, the simple integration with unobtrusive client validation is a nice win too - especially when used with Html helpers.

The sample illustrated above is available as a GitHub repo. It includes the Web Pages 2 assemblies in the bin folder so you can run it from WebMatrix 1 if you haven't upgraded yet.

 

Date Posted: Thursday, June 14, 2012 12:47 PM
Last Updated: Saturday, February 23, 2013 9:30 PM
Posted by: Mikesdotnetting
Total Views to date: 86968

14 Comments

Tuesday, June 19, 2012 6:40 AM - Mike

Wow Web Pages just keep getting better and better! Nice tutorial.

Tuesday, June 19, 2012 8:47 AM - Kevin

Thanks for the post, I don't like the fact though that the validation logic is in the view. Is there a better approach for this?

In MVC currently you use model attributes which is great and seperates this from the view.

How do you see this being implemented in a more seperated way.

Tuesday, June 19, 2012 9:22 AM - Mike

@Kevin,

There is no concept of "View" or "Model" in the Web Pages framework. It is designed as a lightweight Page-centric development model similar to classic ASP or PHP. If you need greater levels of separation, you should use MVC.

Friday, August 10, 2012 6:41 AM - wjx

when the page is in the root directory, It works fine; but when I put it in the Views directory, prompting System.Web.Helpers.Validation doesn’t contain ‘Add’ or ‘RequireField’ definition

Friday, August 10, 2012 7:15 AM - Mike

@wjx,

"Views" directory? Are you writing an MVC app? If so, you are better off using data annotations on your ViewModels for validation. This article covers validation in the Web Pages framework.

Wednesday, October 3, 2012 4:40 PM - Tunde Szabo

How to do validation when posting to another page? When I take out the action validation works (page posts back to itself) but when I try posting to another page, it does not work. I'm a complete newbie. Thanks in advance for any suggestions

Thursday, October 4, 2012 6:22 AM - Mike

@Tzunde

The validators are designed to work with forms that post to the same page.

Tuesday, October 16, 2012 2:39 PM - JT

There is a mistake near the top of the article where you 'specifying that a form field is mandatory'(your words) the Validation code is missing an 'a'.

Tuesday, October 30, 2012 3:37 PM - Law Abiding Citizen

for(;;)
{
Console.Writeln("BIG Thanks")
}

Wednesday, May 29, 2013 11:34 AM - Jaroslav

Hi Mike,
How can I do Validation on server side if I have two forms and I want to validate them individually?
Thanks
Jaro

Monday, November 4, 2013 10:32 AM - Kuberan

Hi, I keep getting this error message when I am using validation to insert into the database. It only happens when if (Validation.IsValid()

I am using web matrix and following the tutrails here:http://www.asp.net/web-pages/tutorials

Is this a program fault or is something wrong with my code:
Exception Details: System.OverflowException: Value was either too large or too small for an Int32.
Line 36:
Line 37: if (IsPost){
Line 38: if (Validation.IsValid()){var insertQuery = "INSERT INTO UserInfo (Name, Surname, IdNum, Address, ContNum, Course, Grade)" + "VALUES (@0, @1, @2, @3 ,@4, @5, @6)";
Line 39: db.Execute(insertQuery, Name, Surname, IDNumber, Address, ContactNumber, Course, grade);
Line 40: }

Monday, January 27, 2014 3:45 AM - Don

Great summary to get started with. Saved me hours of research. I will be passing your link on to my students.

Wednesday, March 5, 2014 9:10 AM - hardik patel

Hey,
when I used this code in my project
Validation.Add doesn't work...
it through an error like System.Web.validation doesn't contain definition like Add.
Please help
Thank you

Wednesday, March 5, 2014 11:08 AM - Mike

@hardik

You need to upgrade to Web Pages 2.
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.