Input Validation With ASP.NET (part 2)

Perform sophisticated pattern validation with the RegularExpressionValidator control.

Moving On

The first part of this series explained the importance of input validation, especially in the context of a Web-based application, and a quick overview of the types of validation controls built into ASP.NET. This was followed up by a whirlwind introduction to some useful validation controls such the RequiredFieldValidator control, RangeValidator control and CompareValidator control.

Moving on from where I left off, this concluding segment explains how you can perform more sophisticated input validation with the remaining two Validator controls, the RegularExpressionValidator control and the CustomValidator control. These two controls are not as simple as the ones you've just seen, so you'll need to have all your wits about you when we start in on them. Additionally, I'll also demonstrate how you can display a summary of input validation errors to the user instead of displaying errors as they occur, on a per-control basis.

Buckle up your seat belts and get ready for a wild ride!

So What's A $#!%% Regular Expression, Anyway?!

Regular expressions, also known as "regex" by the geek community, are a powerful tool used in pattern-matching and substitution. They are commonly associated with almost all *NIX-based tools, including editors like vi, scripting languages like Perl and PHP, and shell programs like awk and sed.

A regular expression lets you build patterns using a set of special characters; these patterns can then be compared with text in a file, data entered into an application, or input from a form filled up by users on a Web site. Depending on whether or not there's a match, appropriate action can be taken, and appropriate program code executed.

For example, one of the most common applications of regular expressions is to check whether or not a user's email address, as entered into an online form, is in the correct format; if it is, the form is processed, whereas if it's not, a warning message pops up asking the user to correct the error. Regular expressions thus play an important role in the decision-making routines of Web applications - although, as you'll see, they can also be used to great effect in complex find-and-replace operations.

A regular expression usually looks something like this:

/war/

All this does is match the pattern "war" in the text it's applied to.

How about something a little more complex? Try this:

/ri+/

This would match the words "ring", "right" and "river-bed". And although it's a pretty silly example, you have to admit that there's truth to it - after all, wasn't the "ring" found "right" at the bottom of the of the "river-bed"?

The "+" that you see above is the first of what are called "meta-characters" - these are characters that have a special meaning when used within a pattern. Similar to the "+" meta-character, we have "*" and "?" - these are used to match zero or more occurrences of the preceding character, and zero or one occurrence of the preceding character, respectively.

In case all this seems a little too imprecise, you can also specify a range for the number of matches. For example, the regular expression

/mat{2,6}/

would match "matting" and "mattress", but not "matrix". The numbers in the curly braces represent the lower and upper values of the range to match; you can leave out the upper limit for an open-ended range match.

Now, regular expressions are a topic in themselves - I don't plan to spend any more time on them (although you certainly can, at http://www.melonfire.com/community/columns/trog/article.php?id=2). Instead, let's look at what this has to do with ASP.NET validation, next.

Regular Joe

Let's take a look at some code that builds on the theory you learnt on the previous page:

<%@ Page Language="C#" %>
<html>
<head>
<title>Registration Form</title>
<basefont face="Arial">
</head>
<body>
<div align="center">
<h1>User Registration Form</h1>

<form runat="server" method="POST" >

<asp:label id="lblUserName" runat="server" text="Username (at least eight alphabetic characters):  " /><asp:textbox id="strUserName" runat="server" ></asp:TextBox><br/>

<asp:RequiredFieldValidator id="strUserNameRFV" ControlToValidate="strUserName" ErrorMessage="Please enter a username!" runat="server" Display="dynamic" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="strUserNameREV" runat="server" ControlToValidate="strUserName" ErrorMessage="Please enter at least eight alphabetic characters!" ValidationExpression="[a-zA-Z]{8,}" display="dynamic" EnableClientScript="false"/><br/>

<asp:button id="Submit" Text="Submit" runat="server"/>

</form>
</div>
</body>
</html>

Once you have previewed this example in the browser, it's time to review the code. First, I have the mandatory <asp:textbox> and <asp:label> server controls to display the form field in the browser. Next, I have the RequiredFieldValidator control, which forces the user to enter a value in the associated server control. It is essential to have this validation control in my ASP.NET form, for reasons which shall be explained in the next few paragraphs.

This brings us to the RegularExpressionValidator validation control which, in addition to the standard "id", "ControlToValidate" and "ErrorMessage" attributes, also comes with a "ValidationExpression" attribute used to assign the regular expression to the validation control. Here, the value "[a-zA-Z]{8,}" is a regex that means "a string consisting of at least eight alphabetic characters".

The other two attributes, "Display" and "EnableClientScript", have been set so that the control is only displayed if there is an error and the validation takes place on the server.

Finally, it's time to explain the reason for using a RequiredFieldValidator control above. Try the following: remove this validation control and submit a blank entry. You will find that no error is reported because of a simple quirk - if there is no data in the text control, the RegularExpressionValidator control does not get activated at all. This quirk means that the end user could end up entering a blank value and the ASP.NET script will not complain at all.

The Number Game

Now that you know how it all works, let's look at a practical example of how you can put this knowledge to good use. Consider the following example, which displays an HTML form asking the user for credit card and email information to complete a purchase.

<%@ Page Language="C#" %>
<html>
<head>
<title>Credit Card Information</title>
<basefont face="Arial">
</head>
<body>
<div align="center">
<h2>Credit Card Information</h2>

<form runat="server" method="POST" >

<!-- credit card number -->

<asp:label id="lblcc_num" runat="server" text="Credit Card Number:  " /><asp:textbox id="cc_num" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_numRFV" ControlToValidate="cc_num" ErrorMessage="Please enter your credit card number!" runat="server" Display="dynamic" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_numREV" runat="server" ControlToValidate="cc_num" ErrorMessage="Please enter the correct credit card number [XXXX-XXXX-XXXX-XXXX]" ValidationExpression="[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}" display="dynamic" EnableClientScript="false"/><br/>

<!-- credit card type -->

<asp:label id="lblcc_type" runat="server" text="Credit Card Type [Mastercard/VISA]:" /><asp:textbox id="cc_type" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_typeRFV" ControlToValidate="cc_type" ErrorMessage="Please select your credit card type!" runat="server" Display="dynamic" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_typeREV" runat="server" ControlToValidate="cc_type" ErrorMessage="Please select Mastercard or a VISA!" ValidationExpression="Mastercard|VISA" display="dynamic" EnableClientScript="false"/><br/>

<!-- Credit Card Date of Expiry -->

<asp:label id="lblcc_doe" runat="server" text="Credit Card Date of Expiry:  " /><asp:textbox id="cc_doe" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_doeRFV" ControlToValidate="cc_doe" ErrorMessage="Please enter the credit card date of expiry!" runat="server" Display="dynamic" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_doeREV" runat="server" ControlToValidate="cc_doe" ErrorMessage="Please enter the correct date of expiry [MM/YYYY]" ValidationExpression="^([0][1-9]|[1][1-2])\/20(0[3-9]|10)$" display="dynamic" EnableClientScript="false"/><br/>

<!-- PIN code of card billing address -->

<asp:label id="lblcc_pin" runat="server" text="PIN code of card billing address:" /><asp:textbox id="cc_pin" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_pinRFV" ControlToValidate="cc_pin" ErrorMessage="Please enter the PIN code!" runat="server" Display="dynamic" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_pinREV" runat="server" ControlToValidate="cc_pin" ErrorMessage="Please enter a correct PIN code (five digits or more)" ValidationExpression="^[0-9]{5,}$" display="dynamic" EnableClientScript="false"/><br/>

<!-- Email Address -->

<asp:label id="lblcc_email" runat="server" text="Email Address:  " /><asp:textbox id="cc_email" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_emailRFV" ControlToValidate="cc_email" ErrorMessage="Please enter your Email Address!" runat="server" Display="dynamic" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_emailREV" runat="server" ControlToValidate="cc_email" ErrorMessage="Please enter a valid email address!" ValidationExpression="^([a-zA-Z0-9])+([\.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-]+)+" display="dynamic" EnableClientScript="false"/><br/>

<asp:button id="Submit" Text="Submit" runat="server"/>

</form>

</body>
</html>

You'll notice, in the example above, that I've used numerous regular expressions to verify that the data being entered into the form by the user is of the correct format. This type of input validation is extremely important on the Web, to ensure that the data you receive is accurate, and in the correct format.

Custom Craft

So far, I have demonstrated four types of validation controls over two tutorials. This next one is the last in the entire series - and probably also the most interesting one of the lot. If you read the introduction, you'll remember the CustomValidator validation control that "allows you to create customized validation routines".

Take a look at the next example that explains client-side validation using a CustomValidator validation control.

<%@ Page Language="C#" %>
<html>
<head>
<title>Odd or Even...</title>

<script language="JavaScript">
function validateNumber(oSrc, args) {
    args.IsValid = (args.Value % 2 == 0);
}
</script>

<basefont face="Arial">
</head>
<body>

<asp:label id="output" runat="server" text="" />

<form runat="server" method="POST" >

<asp:label id="lblMyNumber" runat="server" text="Please enter your favourite even number: " /><asp:textbox id="txtMyNumber" runat="server" />

<asp:RequiredFieldValidator id="txtMyNumberRFV" ControlToValidate="txtMyNumber" ErrorMessage="Please enter a number" runat="server" display="dynamic" />

<asp:CustomValidator id="txtMyNumberCV" runat="server" ClientValidationFunction="validateNumber" ControlToValidate="txtMyNumber" ErrorMessage="Please enter an even number." display="dynamic" />
<br/>

<input type="submit" value="Submit">

</form>
</body>
</html>

Enter a number and tab to the Submit button. The script will immediately warn the user if he/she enters an odd number.

The first step is to add the CustomValidator control to the ASP.NET code and associate it with the server control to be validated.

<%

// snip

<asp:label id="lblMyNumber" runat="server" text="Please enter your favourite even number: " /><asp:textbox id="txtMyNumber" runat="server" />

<asp:RequiredFieldValidator id="txtMyNumberRFV" ControlToValidate="txtMyNumber" ErrorMessage="Please enter a number" runat="server" display="dynamic" />

<asp:CustomValidator id="txtMyNumberCV" runat="server" ClientValidationFunction="validateNumber" ControlToValidate="txtMyNumber" ErrorMessage="Please enter an even number." display="dynamic" />
<br/>

// snip

%>

As you can see above, many of the attributes of this new CustomValidator control are similar to the ones encountered before. However, there is one new attribute, the "ClientValidationFunction" attribute, which is unique to this control. More on this in a moment.

Now, it's time to bring the validateNumber() function to your notice. This JavaScript function is used to check whether the number entered by the user is an even number using a complex equation devised by the Greeks eons ago. Just kidding, folks - it's just good ol' elementary arithmetic at work.

<%

// snip

<script language="JavaScript">
function validateNumber(oSrc, args) {
    args.IsValid = (args.Value % 2 == 0);
}
</script>

// snip

%>

You can name your custom validation function however you like so long it confirms to the following syntax:

function customFunction(oSrc, args) {

    // code comes here
}

Now that we have our custom validation function in place, it's time to associate it with the CustomValidator validation control defined earlier. The "ClientValidationFunction" attribute of the control is used to associate the validateNumber() function with the CustomValidator server control.

Something to note here - more on this when we discuss server side validation using the "CustomValidator" control - is the "IsValid" property. This property is set to false if any the validation controls present in the ASP.NET script fails. We shall see more of this property in action in the next example, which explains server-side validation. Read on.

Bringing In The Server

The previous example demonstrated custom validation using a client side JavaScript function. Let me now take the action to the server where you can define similar custom functions in C# (or any .NET compatible language) on the server.

<%@ Page Language="C#" %>
<html>
<head>
<title>Odd or Even...</title>
<script language="C#" runat=server>

protected void NumberValidate(object source, ServerValidateEventArgs args) {
    args.IsValid = (Convert.ToInt16(args.Value)%2 == 0);
}

void Submit_Click(object source, EventArgs args) {

    if (IsValid) {
        output.Text = "Thank you very much.";
    } else {
        output.Text = "Sorry, an error has occurred.";
    }

}
</script>
<basefont face="Arial">
</head>
<body>

<asp:label id="output" runat="server" text="" />

<form runat="server" method="POST" >

<asp:label id="lblMyNumber" runat="server" text="Please enter your favourite even number: " /><asp:textbox id="txtMyNumber" runat="server" />

<asp:RequiredFieldValidator id="txtMyNumberRFV" ControlToValidate="txtMyNumber" ErrorMessage="Please enter a number" runat="server" display="dynamic" EnableClientScript="false"/>

<asp:CustomValidator id="txtMyNumberCV" runat="server" OnServerValidate="NumberValidate" ControlToValidate="txtMyNumber" ErrorMessage="Please enter an even number." display="dynamic" EnableClientScript="false" />
<br/>

<asp:Button OnClick="Submit_Click" Text="Submit" runat="server" />
</form>
</body>
</html>

As before, this example asks the user to enter an even number. If the user enter valid data, the text in the ASP.NET label control displays a "Thank You" message. And if the user enters an odd number, an error message immediately provides a warning.

Here is a closer look at the code for this example:

<%

// snip

<asp:label id="lblMyNumber" runat="server" text="Please enter your favourite even number: " /><asp:textbox id="txtMyNumber" runat="server" />

<asp:RequiredFieldValidator id="txtMyNumberRFV" ControlToValidate="txtMyNumber" ErrorMessage="Please enter a number" runat="server" display="dynamic" EnableClientScript="false"/>

<asp:CustomValidator id="txtMyNumberCV" runat="server" OnServerValidate="NumberValidate" ControlToValidate="txtMyNumber" ErrorMessage="Please enter an even number." display="dynamic" EnableClientScript="false" />
<br/>

// snip

%>

The only difference between the two examples is that I have used the "OnServerValidate" attribute here instead of the "ClientValidationFunction" attribute used in the previous example. From the nomenclature of the attribute, it is clear that this attribute is used to associate a function with the validation control, in this case the NumberValidate() function.

<%

protected void NumberValidate(object source, ServerValidateEventArgs args) {
    args.IsValid = (Convert.ToInt16(args.Value)%2 == 0);
}

%>

Once again, you can write your own custom code so long as the function follows the following syntax:

protected void MyValidationFunction(object source, ServerValidateEventArgs args) {

    // code comes here
}

Instead of using the vanilla HTML <submit> form control, I have opted for an ASP.NET server control, as shown below.

<%

<asp:Button OnClick="Submit_Click" Text="Submit" runat="server" />

%>

This <asp:button> control comes with a handy "OnClick" attribute that can be used to assign a function. In the example above, I have defined a Submit_Click() function for this purpose.

<%

void Submit_Click(object source, EventArgs args) {
    if (IsValid) {
        output.Text = "Thank you very much.";
    } else {
        output.Text = "Sorry, an error has occurred.";
    }
}

%>

Basically, this function checks the status of the "IsValid" property of the ASP.NET page. I had told you earlier that this property is set to "true" if there are no validation errors or "false" if any of the server control validation rules fail. The Submit_Click() function then utilizes this property to update the text in the label server control with an appropriate message.

A Comedy of Errors

One thing that has been pretty consistent across all the validation controls you've seen so far has been the display of error messages. I have always placed them just under the corresponding server control. Interestingly, there is one more way to display these ugly error messages to the user, and ensure that he/she rectifies them immediately. How? With a ValidationSummary server control that displays all the error messages at one go. Take a look:

<%@ Page Language="C#" %>
<html>
<head>
<title>Credit Card Information</title>
<basefont face="Arial">
</head>
<body>
<div align="center">
<h2>Credit Card Information</h2>

<form runat="server" method="POST" >

<asp:ValidationSummary runat="server" ID="cc_validsumm" ValidationSummaryDisplayMode="bulletlist" HeaderText="Sorry, the form could not be submitted because the following errors occurred:" />

<!-- Credit Card Number -->

<asp:label id="lblcc_num" runat="server" text="Credit Number:  " /><asp:textbox id="cc_num" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_numRFV" ControlToValidate="cc_num" ErrorMessage="Please enter your credit card number!" runat="server" Display="none" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_numREV" runat="server" ControlToValidate="cc_num" ErrorMessage="Please enter the correct credit card number [XXXX-XXXX-XXXX-XXXX]" ValidationExpression="[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}" Display="none" EnableClientScript="false"/><br/>

<!-- Credit Card Type -->

<asp:label id="lblcc_type" runat="server" text="Credit Card Type [Mastercard/VISA]:  " /><asp:textbox id="cc_type" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_typeRFV" ControlToValidate="cc_type" ErrorMessage="Please select your credit card type!" runat="server" Display="none" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_typeREV" runat="server" ControlToValidate="cc_type" ErrorMessage="Please select whether you have a Mastercard or a VISA!!!" ValidationExpression="Mastercard|VISA" Display="none" EnableClientScript="false"/><br/>

<!-- Credit Card Date of Expiry -->

<asp:label id="lblcc_doe" runat="server" text="Credit Card Date of Expiry:  " /><asp:textbox id="cc_doe" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_doeRFV" ControlToValidate="cc_doe" ErrorMessage="Please enter the credit card date of expiry!" runat="server" Display="none" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_doeREV" runat="server" ControlToValidate="cc_doe" ErrorMessage="Please enter the correct date of expiry [MM/YYYY]" ValidationExpression="^([0][1-9]|[1][1-2])\/20(0[3-9]|10)$" Display="none" EnableClientScript="false"/><br/>

<!-- PIN code of card billing address -->

<asp:label id="lblcc_pin" runat="server" text="PIN code of card billing address:  " /><asp:textbox id="cc_pin" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_pinRFV" ControlToValidate="cc_pin" ErrorMessage="Please enter your PIN code!" runat="server" Display="none" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_pinREV" runat="server" ControlToValidate="cc_pin" ErrorMessage="Please enter a correct Pin Code (atleast five digits or more)." ValidationExpression="^[0-9]{5,}$" Display="none" EnableClientScript="false"/><br/>

<!-- Email Address -->

<asp:label id="lblcc_email" runat="server" text="Email Address:  " /><asp:textbox id="cc_email" runat="server" ></asp:textbox><br/>

<asp:RequiredFieldValidator id="cc_emailRFV" ControlToValidate="cc_email" ErrorMessage="Please enter your email address!" runat="server" Display="none" EnableClientScript="false"/>

<asp:RegularExpressionValidator id="cc_emailREV" runat="server" ControlToValidate="cc_email" ErrorMessage="Please enter a valid email address!" ValidationExpression="^([a-zA-Z0-9])+([\.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-]+)+" Display="none" EnableClientScript="false"/><br/>

<asp:button id="Submit" Text="Submit" runat="server"/>

</form>
</body>
</html>

This example is a copy of one of the previous listings, albeit with a few minor changes. In the previous example, the error messages were displayed under the text boxes. However, in this case, they have been grouped together at the top of the page.

Time to focus on the server control that makes this all possible: the ValidationSummary validation control.

<%

<asp:ValidationSummary runat="server" ID="cc_validsumm" ValidationSummaryDisplayMode="bulletlist" HeaderText="Sorry, the form could not be submitted because the following errors occurred:" />

%>

As usual, there are a list of attributes to deal with here. First, the "ValidationSummaryDisplayMode" attribute controls the manner in which the error messages are displayed to the user. This attributes takes three different values - "BulletList", "List" and "Paragraph", all of which are self explanatory.

Next, the "HeaderText" property is used to define the text that appears before the list of the error messages. There are two useful values here -the "ShowMessageBox" value shows the error messages in a neat little message box, while the "ShowSummary" value is a Boolean that controls the display of the error messages in the browser.

But what about the existing validation controls? How come they don't display their respective error messages under the associated text box controls. Simple... I've told them not to, by setting the "Display" attribute of each to "none". This ensures that the error messages are not displayed by the individual validation controls.

And that's about it. Since its time to bid adieu, let me quickly run through what we learnt today for one last time. I started this article with an introduction to the concept of regular expressions, and followed up with a quick overview of the RegularExpressionValidator control, used to check input against a regex associated with each control. The combination of this server-side control with powerful regular expressions ensures that you will always be able to deal with complex form field validation requirements.

This was followed by the CustomValidator control, which allows you to define our own custom validation functions in any .NET compatible language and associate them with server controls. Finally, the ValidationSummary control allows you to present a neat little summary of the errors that occurred when the form was submitted.

With that, I bring this two-part segment on validation controls to a close. I hope you enjoyed reading it as much I did writing it. Till next time, ciao!

Note: Examples are illustrative only, and are not meant for a production environment. Melonfire provides no warranties or support for the source code described in this article. YMMV!

This article was first published on02 Mar 2004.