Chapter 6. Validation
In addition to visual controls, discussed in Chapter 2, which render HTML based on their current state, ASP.NET provides a set of validation controls, which make it simple to add even sophisticated validation to any page. These controls enforce validation constraints on user-populated form data through both client-side and server-side techniques, and are easy to customize in their appearance and their behavior.
6.1 Form Validation
It is common and often critical for Web forms to validate user data entry. Forms are used to collect data from users to be stored in databases, and the types of information stored must match the types of the fields in the database used to store them. Furthermore, there are often dependencies on data entered by a user that should be validated as the data is entered, such as checking to be sure that a user entered a password identically in two separate fields. A good form should strive to make it easy to enter correct data and hard to enter bad data, through a combination of client-side scripting and server-side validation.
Figure 6-1 shows a sample Web form implementing validation. Client-side scripting is used to highlight the fields in which there are errors (the e-mail address is not formatted correctly, and the user neglected to fill in the day phone number). Finally, the form provides a summary of all the errors in a bulleted list on the side, with a request to the user to correct the errors before resubmitting the form.
Figure 6-1. Sample Form with Validation Errors
6.1.1 Client-Side Validation
Form validation can take place on the client side, on the server side, or ideally on both. Validation on the client side is useful because it reduces the number of round-trips necessary for a user to complete a form successfully and can provide immediate feedback to the user as she enters the data (such as highlighting fields that are incorrect in red).
Listing 6-1 shows one example of performing client-side validation with two fields that are validated using client-side scripting. If the user tries to submit the form without entering her name or e-mail address, red text will appear next to the invalid field, indicating that it must be filled in before submitting the form will work.
Listing 6-1 Client-Side Script Validation Example
<!� ClientScriptValidate.htm �> <html> <head> <script language=javascript> function checkForm() { var ret = false; if (document.all["cname"].value == "") document.all["err_cname"].style.visibility = "visible"; else if (document.all["email"].value == "") document.all["err_email"].style.visibility = "visible"; else ret = true; return ret; } </script> </head> <form name="SIGNUP" method="post" onSubmit="return checkForm()"> <table cellspacing=0 cellpadding=1 border=0> <tr valign=top> <td align=right><b>Name:</b></td> <td><input id="cname" /></td> <td><span id="err_cname" style="visibility:hidden;color:red"> Please enter a name here</span></td></tr> <tr valign=top> <td align=right><b>E-mail address:</b></td> <td><input id="email" /></td> <td><span id="err_email" style="visibility:hidden;color:red"> Please enter your email here</span></td></tr> </table> <input type=submit value="sign up!" /> </form> </body> </html>
It is important to note that client-side validation should never be used as the sole source of validation for two reasons. First, it requires that the client browser support scripting, which may not always be the case. And second, client-side scripting can be easily subverted by a malicious user to submit bad data, perhaps corrupting your database. This is why client-side validation is most often used in conjunction with server-side validation.
6.1.2 Server-Side Validation
Before saving data entered by a user, the server should always validate it. Whether that means verifying that an e-mail address has an "@" sign in it or ensuring that the phone number contains only digits, it guarantees that any data you are storing will be consistent. Where you do this validation depends on your server implementation, but it is important for the server to be able to redisplay the form to the user if it does encounter errors.
6.1.3 Validation Observations
Validation is performed in an ad hoc way in most Web applications today. The goal of the ASP.NET validation controls is to provide a generic way to perform validation without compromising flexibility. To that end, it is useful to summarize some of the fundamental elements of Web validation so that we can verify that they are incorporated in the ASP.NET validation control scheme.
Many validation schemes involve placing error messages next to the offending input element. This makes it obvious to the user which field is in error. For convenience, all the problems with the data a user has entered in a form are often summarized in a list or paragraph somewhere on the page. It is important that both client-side and server-side validation be incorporated when possible. For any particular form field, we may want to perform multiple types of validation and display a different error message depending on the particular validation that failed. For example, we might want to display a message such as "Please enter an e-mail address" if the user has neglected to enter an e-mail address, but we would want a message such as "The e-mail address is not formatted correctly" if the user has given us a badly formatted e-mail address. We also want to be able to validate interdependent fields. For example, we may want to verify that a password field and a password verification field contain the same value. Finally, it is convenient to use regular expressions when performing validation.
6.2 Validation Control Architecture
Validation controls provide a simple, yet flexible way of validating form data on both the client and the server. To use a validation control, you add it to your form where you would like an error indicator to be displayed, typically adjacent to the field it is validating. You then associate it with another control on the page to validate, and when the user interacts with the form, the validation control enforces its validation on the control, both on the client, using client-side JavaScript, and the server, all with just one control. As an example, Listing 6-2 shows the use of a RequiredFieldValidator control to validate that a field contains a value. In this case, we are validating that the TextBox with an ID equal to _name is not left blank.
Listing 6-2 Adding a RequiredFieldValidator Control to a Form
<asp:TextBox id="_name" runat=server/> <asp:RequiredFieldValidator ControlToValidate="_name" Display="Static" InitialValue="" runat=server ErrorMessage="The name field must be completed."> *** </asp:RequiredFieldValidator>
Note that the ControlToValidate property of the validation control associates it with the TextBox with the identifier _name in this case. The ErrorMessage property contains the string that is used in the summary of errors for a page. We will see how this is used when we discuss the ValidationSummary control. Finally, the HTML that you place within the validation control (in our case, just the text character "*") is what is displayed when the validation fails. It is your job to place the validation control at the location where this error HTML should appear梫ery often this will be in a table cell adjacent to the input control it is validating. If you neglect to place any HTML in the body of the validation control, it will display the ErrorMessage string both at the location of the control and in the ValidationSummary control, if there is one.
The Display property of a validation control indicates whether the value of this control should occupy space when it is not being shown. If this is set to static, the validation control renders as a span element whose visibility style attribute is toggled between hidden and visible. If it is set to dynamic, the control renders as a span element whose display style is toggled between none and inline. The difference between these two is that setting it to dynamic causes the span element to not occupy space on the form when it is not visible. You may want to specify dynamic if you have more than one validation control associated with a field, only one of which will ever display a message at a time (such as validating that a field is not empty and that it has a correctly formatted telephone number). You could also specify none for the Display property if the control never displayed any HTML error message (perhaps it displays only errors in the summary of errors for a page). Figure 6-2 shows the two renderings of a RequiredFieldValidator control with different Display attribute values.
Figure 6-2. Validation Control Rendering
6.2.1 Page Validation
Adding a validation control to a form enables both client-side (for browsers that support it) and server-side validation. Server-side validation occurs once a page has been loaded and all the controls on the page have had their values restored. The Page class maintains a list of all the validation controls defined on that page in its Validators collection. Each validation control implements the IValidator interface, shown in Listing 6-3. This interface defines two properties, ErrorMessage and IsValid, and a method called Validate().
Listing 6-3 The IValidator Interface
public interface IValidator { string ErrorMessage {get; set;} bool IsValid {get; set;} void Validate(); }
Invoking the Validate() method on a validation control requests the control to execute its validation algorithm. The Page class also exposes a top-level Validate() method that can be used to collectively invoke all Validate() methods on all validation controls on that page. Similarly, the Page class exposes a Boolean property, IsValid, indicating whether or not all the validation controls succeeded their validation on that page. Figure 6-3 shows this validation control architecture.
Figure 6-3. Validation Control Architecture
As soon as you place a validation control on a page, it is imperative that you check the IsValid flag of the Page class before using any of the data posted by the client. It is a common misconception that if validation fails on a page, the code for that page will not execute. On the contrary, the only thing that happens when server-side validation fails is that the IsValid flag of the Page class is set to false, and each validation control that failed renders itself as a visible span so that the error indicator shows up when the page is redisplayed to the user.
It is also important to realize exactly when server-side validation occurs. The IsValid flag will be set properly after the validation controls have had their Validate() routines invoked, which will happen implicitly in the Page class immediately after the Load event fires. This means that if you are looking at data submitted by the user in a handler for the Load event, there is no way for you to know whether it is valid yet or not. Instead, you should work with client-submitted data in server-side event handlers such as the Click event of a Button, at which point the Page will have performed validation, and you can safely check the IsValid property. If you truly need to access client-submitted data in your Load handler, you can always explicitly invoke the Validate() method of your Page class. Figure 6-4 shows the complete post-back sequence and where validation occurs in that sequence.
Figure 6-4. Server-Side Validation During Post-Back
6.2.2 Client-Side Validation
If you try accessing a page with validation controls, you will notice that it performs validation within the browser and does not even submit the page until all the validated fields have acceptable data. ASP.NET performs client-side validation implicitly when you add validation controls to a page, as long as the client browser supports it.
As part of the HTML rendering of a Page with validation controls, ASP.NET generates a script reference to the file WebUIValidation.js, which contains a collection of validation routines. It also generates embedded client-side JavaScript in the rendered HTML page that interacts with the imported validation routines. Figure 6-5 shows the interaction of the various script elements involved with client-side validation.
Figure 6-5. Script Elements of Client-Side Validation
One of the JavaScript elements that the Page class generates when it contains validation controls is a global array named Page_Validators. This array is initialized to contain references to all the span elements rendered by validation controls. Similarly, another global array, named Page_ValidationSummaries, contains all the ValidationSummary controls (usually just one) for that page. All the routines in the WebUIValidation.js file depend on these two arrays being populated when the page loads. The other thing a Page does when it renders with validation controls is to add an explicit call to Page_ClientValidate() in any controls that perform post-backs (typically buttons). Finally, an explicit call to ValidatorOnLoad() is injected into the rendered page that is called when the page loads.
In the implementation of ValidatorOnLoad(), each of the span elements in the Page_Validators array is accessed, and the ControlToValidate attribute is extracted. This control is then passed into a function called ValidatorHookupControl, in which its onchange event is wired up to the ValidatorOnChange() function. Now the actual validation can happen at one of two times. First, it always happens when the user explicitly posts the page back. When the entire page is submitted, the Page_ClientValidate() routine is called, which iterates across all the validators, invoking their corresponding evaluation functions, and if any one of them fails, the submission of the form is aborted and the validation error messages are displayed. The other time validation can happen is when a control that has an associated validator loses focus and fires its OnChange event. This fires only the validator associated with that control. There is also logic in place to avoid enforcing RequiredFieldValidator controls until the page has been submitted (or has attempted to be submitted) at least once. This allows users to fill in fields in any order without being warned that a field is required.
6.3 Validation Controls
ASP.NET provides six built-in validation controls. These controls provide four types of validation, as well as a way to summarize errors and a way to construct your own validation algorithms.
The BaseValidator class is used as the base class for all the validation controls, providing a single generic implementation of many of the validation capabilities. Listing 6-4 shows a partial definition of the class. Note that it implements the IValidator interface and derives from Label. By deriving from the Label control, the BaseValidator class inherits the span-rendering capabilities of that class, which is convenient because all validation controls render as span elements. In addition, the BaseValidator class defines the ErrorMessage property used by the ValidationSummary control to display the collection of error messages for a form. It also defines the ForeColor property, which lets you change the color of the validation text (it defaults to red). The Enabled property of the BaseValidator class is a convenient way to disable individual validation controls.
Listing 6-4 The BaseValidator Class
public abstract class BaseValidator : Label, IValidator { protected BaseValidator(); // Properties public static PropertyDescriptor GetValidationProperty(object component) {} public ValidatorDisplay Display {get; set; } public bool Enabled {override get; override set; } public string ErrorMessage {get; set; } public Color ForeColor {override get; override set; } public bool IsValid {get; set; } // Methods public void Validate(); protected void CheckControlValidationProperty( string name, string propertyName); protected abstract bool EvaluateIsValid(); protected string GetControlRenderID(string name); protected string GetControlValidationValue(string name); protected void RegisterValidatorCommonScript(); protected void RegisterValidatorDeclaration(); //... }
For a control to be validated with the validation controls, it must have the ValidationPropertyAttribute attribute set to the field that is to be validated. Validation controls apply only to controls that can define a single field that is to be validated, so controls such as grids cannot be used with validation controls. In addition, for the client-side validation to work for a control, its value attribute must evaluate to the field that is to be validated, because the validation functions operate under this assumption. Figure 6-6 shows a list of all the controls that can be validated in ASP.NET.
Figure 6-6. Controls That Can Be Used with Validation Controls
Six validation controls are available in ASP.NET. The RequiredFieldValidator, as we have seen already, is used to verify that a field is not left empty. The ValidationSummary control is used to display a summary of all the error messages on a given form and optionally to display a client-side message box. The CompareValidator control is used for comparing the value of one field to the value of another field (or a constant). The RangeValidator control is used to verify that a field value falls within a given range. The RegularExpressionValidator control is used to verify the contents of a field against a regular expression, and finally the CustomValidator control is used for executing your own validation algorithms.
It is important to note that for all these controls except the RequiredFieldValidator, an empty field is considered valid. Thus, it is common to combine the RequiredFieldValidator control with other validation controls to enforce both population and some other form of validation. Table 6-1 shows the properties of each of the six validation controls and their associated values.
Validation Control |
Properties |
Values/Description |
---|---|---|
RequiredFieldValidator |
- |
- |
CompareValidator |
Operator |
Equal, NotEqual, GreaterThan,GreaterThanEqual, LessThan, LessThanEqual, DataTypeCheck |
Type |
String, Currency, Date, Double, Integer |
|
ValueToCompare |
Constant value to compare to |
|
ControlToCompare |
Other control to compare to |
|
RangeValidator |
MaximumValue |
Constant value for upper bound |
MinimumValue |
Constant value for lower bound |
|
Type |
String, Currency, Date, Double, Integer |
|
RegularExpressionValidator |
Validation Expression |
Regular expression to validate against |
CustomValidator |
ServerValidate |
Server-side validation routine |
ClientValidationFunction |
Client-side validation routine |
|
ValidationSummary |
DisplayMode |
BulletList, List, SingleParagraph |
HeaderText |
Title text of summary |
|
ShowMessageBox |
Display alert? |
|
ShowSummary |
Display summary paragraph? |
The ValidationSummary control culls all the ErrorMessages of each validation control on a page and displays them in a bullet list, a plain list, or a single paragraph format. Listing 6-5 shows the ValidationSummary control and its additional properties. The HeaderText property defines the title string for the summary of error messages. The ShowMessageBox property causes a message box with the summary of errors to appear when the user tries to submit a form that has validation errors. The ShowSummary property turns the summary on or off (you may want to turn it off if you elect to use the ShowMessageBox property). Finally, the DisplayMode property selects which of the three types of display you would like the summary to appear as. Listing 6-5 shows an example of using the ValidationSummary control, along with two RequiredFieldValidator controls to validate the text inputs.
Listing 6-5 ValidationSummary Control Example
<!� File: ValidationSummary.aspx �> <html><body> <form runat=server> <table cellspacing=0 cellpadding=2 border=0> <tr><td><table cellspacing=0 cellpadding=1 border=0> <tr><td align=right><b>Name:</b></td> <td><asp:TextBox id="cname" runat=server/></td> <td><asp:RequiredFieldValidator id="cnameValidator" ControlToValidate="cname" Display="Static" ErrorMessage="Name must be filled in." InitialValue="" runat=server>* </asp:RequiredFieldValidator> </td></tr> <tr><td align=right><b>Phone:</b></td> <td><asp:TextBox id="phone" runat=server/></td> <td><asp:RequiredFieldValidator id="phoneValidator" ControlToValidate="phone" Display="Static" ErrorMessage="Phone must be filled in." InitialValue="" runat=server>* </asp:RequiredFieldValidator> </td></tr> <tr><td></td> <td><input value="Enter" type=submit /></td> </tr></table><td> <asp:ValidationSummary id="valSum" runat=server HeaderText="Please correct the following errors:" ShowMessageBox="True"/></td></tr> </table> </form> </body></html>
Listing 6-6 shows the CompareValidator control and its additional properties. The Operator property defines what comparison operation to perform, and the Type property specifies the type of the two values to compare. Either the ValueToCompare or the ControlToCompare property contains the value (or reference to a field containing the value) to compare with the target input. Listing 6-6 shows an example of using the CompareValidator control to verify that the user is over 21 years of age.
Listing 6-6 CompareValidator Control Example
<!� File: CompareValidator.aspx �> <%@ Page %> <html> <body> <h1>Compare Validator Example</h1> <form runat="server"> Age: <asp:TextBox id="_age" runat=server/> <asp:CompareValidator id="ageValidator" ControlToValidate="_age" ValueToCompare="21" Type="Integer" Operator="GreaterThan" runat=server> You must be over 21! </asp:CompareValidator> <br/> <input value="Enter" type=submit /> </form> </body> </html>
Listing 6-7 shows the RegularExpressionValidator control with its one additional property: ValidationExpression. With this control, you can specify any JavaScript regular expression to validate a field. Listing 6-7 shows an example of validating a zip code to ensure that it is of the correct format. Table 6-2 shows some of the more common regular expression characters and their meaning.
Listing 6-7 RegularExpressionValidator Control Example
<!� File: RegularExpressionValidator.aspx �> <%@ Page %> <html> <body> <h1>Regular Expression Validator Example</h1> <form runat="server"> Zipcode:<asp:TextBox id="_zipcode" runat=server/><asp:RegularExpressionValidator ControlToValidate="_zipcode" Display="static" ErrorMessage="Zip code must be of the form 11111-1111" InitialValue="" width="100%" runat=server ValidationExpression="\d{5}(-\d{4})?">** </asp:RegularExpressionValidator> <br/> <input value="Enter" type=submit /> </form> </body> </html>
Character |
Meaning |
---|---|
[...] |
Match any one character between brackets |
[^...] |
Match any one character not between brackets |
\w |
Match any word character [a杬A朲0� |
\W |
Match any whitespace character [^ \t\n\r\f\v] |
\s |
Match any non-whitespace character [^ \t\n\r\f\v] |
\d |
Match any digit [0�/P> |
\D |
Match any nondigit character [^0�/P> |
[\b] |
Match a literal backspace |
{n,m} |
Match the previous item >= n times, <= m times |
{n,} |
Match the previous item >= n times |
{n} |
Match the previous item exactly n times |
? |
Match zero or one occurrence of the previous item {0,1} |
+ |
Match one or more occurrences of the previous item {1,} |
* |
Match zero or more occurrences of the previous item {0,} |
| |
Match the subexpression either on the left or the right |
(...) |
Group items together in a unit |
^ |
Match the beginning of the string |
$ |
Match the end of the string |
\b |
Match a word boundary |
\B |
Match a position that is not a word boundary |
The CustomValidator control lets you define your own validation function to perform whatever arbitrary validation you would like on a field. In keeping with the other validation controls, you can provide both a client-side validation routine (in JavaScript) and a server-side validation routine. Listing 6-8 shows an example of using a CustomValidator control to verify that a number is divisible by three. Note that two script sections are defined梠ne client script and one server script梕ach containing a validation function that performs the same check on the incoming data. The client-side validation function is specified in the ClientValidationFunction property of the control, and the server-side validation function is specified as the event handler for the ServerValidate event by assigning the function name to the OnServerValidate property of the control. Note that the form of the validation function is always the same: bool ValFunc(object source, object args);. The object parameter is the validator element, and the args parameter is an object containing two properties, Value and IsValid.
Listing 6-8 CustomValidator Control Example
<!� File: CustomValidator.aspx �> <%@ Page Language="C#" %> <script language=javascript> <!� function ValModThreeClient(source, args) { if (args.value % 3) args.IsValid=false; else args.IsValid=true; } �> </script> <script language="C#" runat=server> void ValModThreeServer(object source, ServerValidateEventArgs e) { e.IsValid = false; try { int num = Convert.ToInt32(e.Value); if (num % 3 == 0) e.IsValid = true; } catch (Exception) {} } </script> <html> <body> <h1>Custom Validator Example</h1> <form runat="server"> Number: <asp:TextBox id="_num" runat=server/> <asp:CustomValidator id="numValidator" ControlToValidate="_num" Display="static" InitialValue="" ClientValidationFunction = "ValModThreeClient" OnServerValidate = "ValModThreeServer" width="100%" ErrorMessage="Please enter a value divisible by three" runat=server/> <br/> <input value="Enter" type=submit /> </form> </body> </html>SUMMARY
Validation controls in ASP.NET relieve the tedium of enforcing validation on a page. Simply placing a validation control on a page and pointing it to another control to validate provides both client-side validation for clients that support it and server-side validation each time the page is posted back. Four validation controls are provided for performing standard validation tasks such as checking that required fields have been populated, comparing the value of one field with another, verifying that the contents of a field fall within a given range, and performing a regular expression check on the contents of a field. Another control is available to generate a list of errors encountered on a page, either in paragraph summary form, as an alert dialog box, or both. Finally, if none of these controls perform the validation you need, a custom validation control lets you specify whatever validation algorithms you want on both the client and the server.