Validation Controls

Six of the controls found in System.Web.UI.WebControls are provided to help you validate user input in Web forms. Known as validation controls, or simply validators, these controls are some of the most interesting—and potentially useful—of all the controls in ASP.NET. Here are two reasons why:

Why do validation controls recheck the input on the server? One, rechecking prevents spoofing. Two, it enables the input to undergo a validation check even if it originated from a browser that doesn’t support client-side scripting.

Validating user input on the client side is a Good Thing because it prevents postbacks from occurring that would ultimately be rejected by the server anyway. Checking on the client side is nothing new; Web programmers have been doing it for years. Unfortunately, writing client-side validation scripts requires a non-trivial knowledge of Dynamic HTML and client-side scripting. Validation controls lower the barrier to entry by encapsulating the required logic in easy-to-use classes. You simply attach validators to the controls you want to validate, and the validators do the rest.

What kinds of checks are validation controls capable of performing? For starters, they can verify that

If none of these validation types fit the bill, you can use a CustomValidator control to enact validation routines of your own. And the ValidationSummary control enables error messages provided by other validators to be displayed as a group.

One nuance you should be aware of regarding validator controls is that the client-side validation code they emit doesn’t work with all browsers. The JavaScript that they return makes extensive use of the browser’s DHTML document object model (DOM). Before the W3C standardized the DOM, Internet Explorer and Netscape Navigator implemented incompatible DOMs. Client-side validation works fine in Internet Explorer 4 and later. ASP.NET sniffs out the browser type and doesn’t even attempt to return client-side validation code to Navigator.

The good news is that the incompatibilities affect only client-side validation. Server-side validation works regardless of the browser type, so someone using a down-level browser won’t be able to submit invalid data without you knowing about it. They will, however, incur a round trip to the server and back each time they try.

Using the Validation Controls

All validator control classes with the exception of ValidationSummary derive from a common base class—BaseValidator—and therefore share many traits, including the following:

Here’s an example demonstrating how some of these properties are used. The following code declares a TextBox and attaches a RequiredFieldValidator to it. It also specifies the error message displayed if the validator fires, ensures that space is reserved for the error message even if it isn’t displayed (possibly preventing the page layout from changing if the error message does appear), and sets the error message’s color to blue:

<asp:TextBox?ID="Password" TextMode="Password" RunAt="server" />

<asp:RequiredFieldValidator
??ControlToValidate="Password"
??ErrorMessage="Required?field"
??Display="static"
??ForeColor="blue"
??RunAt="server"
/>

If you’d prefer, you can omit the ErrorMessage attribute and place the error message between the validator’s start and end tags:

<asp:RequiredFieldValidator
??ControlToValidate="Password"
??Display="static"
??ForeColor="blue"
??RunAt="server"
>
Required?Field
</asp:RequiredFieldValidator>

The downside to defining an error message this way is that it prevents the error message from working in conjunction with ValidationSummary controls. The upside is that it lets you create “rich” error messages that include HTML tags.

A validator can be attached to only one control, but any number of validator controls can be declared on a page. In addition, a given control can have several validators attached to it. The following example combines a RequiredFieldValidator and a RegularExpressionValidator to verify that a password field isn’t blank and that it contains at least eight characters:

<asp:TextBox?ID="Password" TextMode="Password" RunAt="server" />

<asp:RequiredFieldValidator
??ControlToValidate="Password"
??ErrorMessage="Required?field"
??Display="dynamic"
??ForeColor="blue"
??RunAt="server"
/>

<asp:RegularExpressionValidator
??ControlToValidate="Password"
??ValidationExpression=".{8,}"
??ErrorMessage="You?must?enter?at?least?8?characters"
??Display="dynamic"
??ForeColor="blue"
??RunAt="server"
/>

At first glance, it might seem as if the RequiredFieldValidator is superfluous because the RegularExpressionValidator verifies that the TextBox contains at least eight characters. However, RegularExpressionValidator performs validation checks only on nonblank fields. The same is true of other validators as well. Therefore, if you want to verify that an input field isn’t blank and that it meets other validation criteria too, you must combine RequiredFieldValidator with other validation controls.

In the preceding example, note that the validators’ Display properties are set to Dynamic rather than Static. The reason? If you write Display=“static,” the RegularExpressionValidator’s error message, if displayed, appears far to the right of the TextBox because of the space reserved for the RequiredFieldValidator’s error message. Display=“dynamic” prevents either control from claiming space that might be used by the other and therefore enables either error message to appear in the same place.

RequiredFieldValidator

As I mentioned, the RequiredFieldValidator control validates input by verifying that the corresponding field isn’t blank. It’s perfect for TextBox controls representing required fields in a form because if any of those TextBoxes is blank, the RequiredFieldValidator won’t allow the form to be submitted to the server.

Here’s an example that verifies that a TextBox named “EMail” isn’t blank:

<asp:TextBox?ID="EMail" RunAt="server" />

<asp:RequiredFieldValidator
??ControlToValidate="EMail"
??ErrorMessage="Required?field"
??Display="static"
??RunAt="server"
/>

Because required fields are so common in Web forms, RequiredFieldValidator has the potential to see more use than all the other validation controls combined. It’s often used in conjunction with other validation controls because none of the others run their validation checks on blank fields.

RangeValidator

The RangeValidator control is the answer to the question, “How do I ensure that an input value falls within a specified range?” Suppose you ask the user to type in a percentage and that valid values range from 0 to 100, inclusive. Here’s how to reject any numbers that fall outside that range:

<asp:TextBox?ID="Percent" RunAt="server" />

<asp:RangeValidator
??ControlToValidate="Percent"
??MinimumValue="0"
??MaximumValue="100"
??Type="Integer"
??ErrorMessage="Value?out?of?range"
??Display="static"
??RunAt="server"
/>

The Type attribute tells a RangeValidator what type of data to use in the comparison. If you don’t specify a type, Type defaults to String. Type=“Integer” performs a numeric comparison involving integers. Other valid Type attributes include Currency, Date, and Double. The following example displays an error message if the date typed into a TextBox doesn’t fall in the fourth quarter of 2002:

<asp:TextBox?ID="MyDate" RunAt="server" />

<asp:RangeValidator
??ControlToValidate="MyDate"
??MinimumValue="10/01/2002"
??MaximumValue="12/31/2002"
??Type="Date"
??ErrorMessage="Date?out?of?range"
??Display="static"
??RunAt="server"
/>

In practice, checking a range of dates with RangeValidator is of limited usefulness because well-designed sites permit users to pick dates from a calendar. Manually entered dates are problematic because they’re culture-sensitive (many parts of the world put days before months, for example) and because RangeValidator understands a limited set of date formats.

CompareValidator

The CompareValidator control validates input by comparing it to a constant value specified with a ValueToCompare attribute or to a value in another control identified with a ControlToCompare attribute. ControlToCompare makes CompareValidator a valuable control for validating input based on other input.

Suppose you invite the user to enter minimum and maximum values denoting the ends of a range. It doesn’t make sense to accept a maximum that’s less than the minimum, so you could use a CompareValidator to ensure the integrity of the input. In the following example, the validator signals an error if the maximum is less than the minimum. The Operator attribute specifies that the value entered in the TextBox named “Maximum” must be equal to or greater than the value entered in the TextBox named “Minimum.” The Type attribute identifies the type of data involved in the comparison:

<asp:TextBox?ID="Minimum" RunAt="server" />
<asp:TextBox?ID="Maximum" RunAt="server" />

<asp:CompareValidator
??ControlToValidate="Maximum"
??ControlToCompare="Minimum"
??Type="Integer"
??Operator="GreaterThanEqual"
??ErrorMessage="Invalid?maximum"
??Display="static"
??RunAt="server"
/>

Other supported values for Operator are Equal, NotEqual, GreaterThan, Less-Than, LessThanEqual, and DataTypeCheck. The last of these values validates the type of data entered by verifying that it matches the type specified in the Type attribute. If you simply wanted to verify that the user input an integer, for example, you could set Type to Integer and Operator to DataTypeCheck.

Another use for CompareValidator is to compare two passwords on pages that ask the user to enter a password and then enter it again to validate the first. Here’s the code to compare two passwords:

<asp:TextBox?ID="Password1" TextMode="Password" RunAt="server" />
<asp:TextBox?ID="Password2" TextMode="Password" RunAt="server" />

<asp:CompareValidator
??ControlToValidate="Password2"
??ControlToCompare="Password1"
??Type="String"
??Operator="Equal"
??ErrorMessage="Mismatch"
??Display="static"
??RunAt="server"
/>

In scenarios like this one, it’s important to validate the second of the two inputs rather than the first to prevent the CompareValidator from firing when the focus moves from the first TextBox to the second.

Be aware that string comparisons performed by CompareValidator are case-sensitive. With passwords that’s typically what you want, but for some forms of text input, you might not want the comparison to be case-sensitive.

RegularExpressionValidator

The most versatile validation control by far is the RegularExpressionValidator, which validates input by verifying that it conforms to a format specified in a regular expression. You can use a RegularExpressionValidator to perform a variety of common validation chores, from verifying that an input value contains only numbers to ensuring that e-mail addresses, zip codes, and credit card numbers are well formed.

Here’s a very simple example—one that uses a RegularExpressionValidator to reject input containing nonnumeric characters:

<asp:TextBox?ID="Quantity" RunAt="server" />

<asp:RegularExpressionValidator
??ControlToValidate="Quantity"
??ValidationExpression="^\d+$"
??ErrorMessage="Digits?only"
??Display="static"
??RunAt="server"
/>

In a regular expression, \d represents the digits 0–9 and + means “one or more of.” The expression \d+ means “one or more characters that are digits.” If the user tries to slip in a string with a letter in it, the validator will reject it.

The next example validates an e-mail address. It can’t actually verify that the address is valid, but it can (and does) verify that the address is well formed:

<asp:TextBox?ID="EMail" RunAt="server" />

<asp:RegularExpressionValidator
??ControlToValidate="EMail"
??ValidationExpression="^[\w\.-]+@[\w-]+\.[\w\.-]+$"
??ErrorMessage="Invalid?e-mail?address"
??Display="static"
??RunAt="server"
/>

Let’s dissect the regular expression. ^ means “start at the beginning of the string.” That’s important because otherwise a string with a substring matching the regular expression (for example, “!@#$%^&* name@domain.com”) would fool the validator into validating the input. The same goes for the $ at the end: it prevents “name@domain.com !@#$%^&*” from resulting in a false match. [\w\.-]+ means “one or more of the following characters: letters, numbers, underscores, periods, and hyphens.” @ means the characters must be followed by an @ sign. [\w-]+ indicates that the @ sign must be followed by one or more letters, numbers, underscores, or hyphens, and \. means all that must be followed by a period. Finally, [\w\.-]+ stipulates that the string must end with one or more letters, numbers, underscores, or hyphens. Strings such as “jeffpro@wintellect.com” and “jeff.pro@win-tellect.com” pass the test just fine, but “jeffpro,” “jeffpro@wintellect,” and “jeffpro@**&&^^%%.com” do not.

CustomValidator

When none of the other validators fits the bill, CustomValidator will do the job that no others can. As its name implies, CustomValidator validates input using an algorithm that you supply. If you want CustomValidator to check input on the client side, you provide a JavaScript function to do the checking and identify the function with a ClientValidationFunction attribute. (You can use VBScript if you’d like, but realize that doing so sacrifices compatibility with browsers other than Internet Explorer. Future versions of ASP.NET will probably support client-side validation in selected third-party browsers.) If you want the input checked on the server as well, you provide the validation method and identify it with an OnServerValidate attribute. The following example uses CustomValidator to verify that an input value is an even multiple of 10:

<asp:TextBox?ID="Amount" RunAt="server" />

<asp:CustomValidator
??ControlToValidate="Amount"
??ClientValidationFunction="__validateAmount"
??OnServerValidate="ValidateAmount"
??ErrorMessage="Amount?must?be?a?multiple?of?10"
??Display="static"
??RunAt="server"
/>
????.
????.
????.
<script?language="JavaScript">
<!--
??function?__validateAmount?(source,?args)
??{
??????args.IsValid?=?(args.Value?%?10?==?0);
??}
-->
</script>

<script?language="C#" runat="server">
??void?ValidateAmount?(Object?sender,?ServerValidateEventArgs?e)
??{
??????try?{
??????????e.IsValid?=?(Convert.ToInt32?(e.Value)?%?10?==?0);
??????}
??????catch?(FormatException)?{
??????????//?In?case?a?non-numeric?value?is?entered
??????????e.IsValid?=?false;
??????}
??}
</script>

The key is to set args.IsValid to true or false on the client side and e.IsValid to true or false on the server side to indicate whether the input is valid. CustomValidator can be used to apply any validation algorithm you want as long as you’re willing to write the code to back it up.

ValidationSummary

ValidationSummary is a different sort of validation control. It doesn’t perform any validation of its own. It does, however, offer an alternative method for displaying other validators’ error messages by “batching them up” and displaying them as a group. The following example uses a ValidationSummary control to summarize all the validation errors encountered on the page. Note that the other validator controls’ Display attributes are set to None, which prevents them from displaying error messages on their own. Before the page is submitted to the server, the ValidationSummary control checks the other validators. If any of the validation checks failed, the ValidationSummary control displays the corresponding error messages in a bulleted list:

<asp:TextBox?ID="UserName" RunAt="server" />

<asp:RequiredFieldValidator
??ControlToValidate="UserName"
??ErrorMessage="The?user?name?can't?be?blank"
??Display="none"
??RunAt="server"
/>

<asp:TextBox?ID="Password" TextMode="Password" RunAt="server" />

<asp:RequiredFieldValidator
??ControlToValidate="Password"
??ErrorMessage="The?password?can't?be?blank"
??Display="none"
??RunAt="server"
/>

<asp:RegularExpressionValidator
??ControlToValidate="Password"
??ValidationExpression=".{8,}"
??Display="none"
??ErrorMessage="The?password?must?contain?at?least?8?characters"
??RunAt="server"
/>

<asp:ValidationSummary
??DisplayMode="BulletList"
??HeaderText="This?page?contains?the?following?errors"
??RunAt="server"
/>

The ValidationSummary control’s DisplayMode attribute can be set to BulletList, which displays error messages in a bulleted list; List, which displays error messages in a list without bullets; or SingleParagraph, which displays error messages without bullets or line breaks. HeaderText specifies the text, if any, that appears above the error messages.

A ValidationSummary control’s ShowMessageBox and ShowSummary properties can be used to further customize the control’s output. By default, ShowMessageBox is false and ShowSummary is true, meaning that error messages are displayed in the Web page. However, setting ShowMessageBox to true and ShowSummary to false displays the error messages in a pop-up message box (what JavaScript programmers refer to as an alert box because of the JavaScript command used to display it: alert):

<asp:ValidationSummary
??DisplayMode="BulletList"
??HeaderText="This?page?contains?the?following?errors"
??ShowMessageBox="true"
??ShowSummary="false"
??RunAt="server"
/>

Setting both ShowMessageBox and ShowSummary to true causes the validation summary to appear in a message box and in the Web page. (Talk about getting someone’s attention!)

Spammers, Incorporated

Ever been spammed? Just in case you don’t get enough spam already, the application in Figure 6-21 displays a form from a fictitious company named Spammers, Inc., that invites users to enter a name, e-mail address, and other personal information and receive spam in return. It collects most of its input from TextBox controls and validates the input using an assortment of RequiredFieldValidators and RegularExpressionValidators. Specifically, it uses the following validators:

Figure 6-20 shows the Spammers, Inc., home page after several validators have fired.

Figure 6-20
The Spammers, Inc., home page.

An item of interest concerning SpammersInc.aspx’s implementation is that the OnSignMeUp method, which executes on the server when the Sign Me Up button is clicked, does nothing if IsValid is false:

void?OnSignMeUp?(Object?sender,?EventArgs?e)
{
????if?(IsValid)?{
??????...
????}
}

IsValid is a System.Web.UI.Page property. When the client is Internet Explorer 4 or later, checking IsValid is redundant unless you’re concerned about someone intentionally circumventing the client-side script in order to submit invalid data. When the client is any other browser (particularly Netscape Navigator), checking IsValid is essential if you want input to be validated. Validation controls don’t do any input checking on the client side in those browsers, so if you fail to check IsValid on the server side, bad input can slip by undetected. IsValid is set to false on the server if any of the page’s validation controls flags an error. An IsValid value equal to true means all of the page’s input conforms to the criteria specified by the validators. If you want validation controls to work regardless of browser type, always check the page’s IsValid property on the server side just in case the input wasn’t validated on the client side.

SpammersInc.aspx
<html>
??<head>
????<style>
????<!--
??????body?{?font:?10pt?verdana?};
??????table?{?font:?10pt?verdana?};
??????input?{?font:?10pt?verdana?};
????-->
????</style>
??</head>
??<body>
????<table?cellpadding="4" border="1">
??????<tr?bgcolor="yellow">
????????<td>
??????Hi!?We're?Spammers,?Incorporated.?If?you'll?provide?us?with?an
??????e-mail?address,?we'll?clog?your?inbox?with?email.?
??????Leave?a?snail?mail?address?and?we'll?bombard?you?with?paper
??????mail,?too.?If?you're?a?totally?trusting?person,?type?in?a?
      credit?card?number.?We'll?use?it?to?defray?office?costs?
??????next?month.
????????</td>
??????</tr>
????</table>
????<h3>Yes,?I?want?to?be?spammed.?Sign?me?up?now!</h3>
????<form?runat="server">
??????<table?cellpadding="4">
????????<tr>
??????????<td?align="right">
????????????Name
??????????</td>
??????????<td>
????????????<asp:TextBox?ID="Name" RunAt="server" />
??????????</td>
??????????<td>
????????????<asp:RequiredFieldValidator
??????????????ControlToValidate="Name"
??????????????ErrorMessage="Please?enter?your?name"
??????????????Display="dynamic"
??????????????RunAt="server"
????????????/>
??????????</td>
????????</tr>
????????<tr>
??????????<td?align="right">
????????????E-Mail?Address
??????????</td>
??????????<td>
????????????<asp:TextBox?ID="EMail" RunAt="server" />
??????????</td>
??????????<td>
????????????<asp:RequiredFieldValidator
??????????????ControlToValidate="EMail"
??????????????ErrorMessage="Please?enter?your?e-mail?address"
??????????????Display="dynamic"
??????????????RunAt="server"
????????????/>
????????????<asp:RegularExpressionValidator
??????????????ControlToValidate="EMail"
??????????????ValidationExpression="^[\w\.-]+@[\w-]+\.[\w\.-]+$"
??????????????ErrorMessage="Invalid?e-mail?address"
??????????????Display="dynamic"
??????????????RunAt="server"
????????????/>
??????????</td>
????????</tr>
????????<tr>
??????????<td?align="right">
????????????Address
??????????</td>
??????????<td>
????????????<asp:TextBox?ID="Address" RunAt="server" />
??????????</td>
??????????<td>
??????????</td>
????????</tr>
????????<tr>
??????????<td?align="right">
????????????City
??????????</td>
??????????<td>
????????????<asp:TextBox?ID="City" RunAt="server" />
??????????</td>
??????????<td>
??????????</td>
????????</tr>
????????<tr>
??????????<td?align="right">
????????????State
??????????</td>
??????????<td>
????????????<asp:DropDownList?ID="StateList" RunAt="server">
??????????????<asp:ListItem?Text="AL" RunAt="server" />
??????????????<asp:ListItem?Text="AK" RunAt="server" />
??????????????<asp:ListItem?Text="AR" RunAt="server" />
??????????????<asp:ListItem?Text="AZ" RunAt="server" />
??????????????<asp:ListItem?Text="CA" RunAt="server" />
??????????????<asp:ListItem?Text="CO" RunAt="server" />
??????????????<asp:ListItem?Text="CT" RunAt="server" />
??????????????<asp:ListItem?Text="DC" RunAt="server" />
??????????????<asp:ListItem?Text="DE" RunAt="server" />
??????????????<asp:ListItem?Text="FL" RunAt="server" />
??????????????<asp:ListItem?Text="GA" RunAt="server" />
??????????????<asp:ListItem?Text="HI" RunAt="server" />
??????????????<asp:ListItem?Text="IA" RunAt="server" />
??????????????<asp:ListItem?Text="ID" RunAt="server" />
??????????????<asp:ListItem?Text="IL" RunAt="server" />
??????????????<asp:ListItem?Text="IN" RunAt="server" />
??????????????<asp:ListItem?Text="KS" RunAt="server" />
??????????????<asp:ListItem?Text="KY" RunAt="server" />
??????????????<asp:ListItem?Text="LA" RunAt="server" />
??????????????<asp:ListItem?Text="MA" RunAt="server" />
??????????????<asp:ListItem?Text="MD" RunAt="server" />
??????????????<asp:ListItem?Text="ME" RunAt="server" />
??????????????<asp:ListItem?Text="MI" RunAt="server" />
??????????????<asp:ListItem?Text="MN" RunAt="server" />
??????????????<asp:ListItem?Text="MO" RunAt="server" />
??????????????<asp:ListItem?Text="MS" RunAt="server" />
??????????????<asp:ListItem?Text="MT" RunAt="server" />
??????????????<asp:ListItem?Text="NC" RunAt="server" />
??????????????<asp:ListItem?Text="ND" RunAt="server" />
??????????????<asp:ListItem?Text="NE" RunAt="server" />
??????????????<asp:ListItem?Text="NH" RunAt="server" />
??????????????<asp:ListItem?Text="NJ" RunAt="server" />
??????????????<asp:ListItem?Text="NM" RunAt="server" />
??????????????<asp:ListItem?Text="NV" RunAt="server" />
??????????????<asp:ListItem?Text="NY" RunAt="server" />
??????????????<asp:ListItem?Text="OH" RunAt="server" />
??????????????<asp:ListItem?Text="OK" RunAt="server" />
??????????????<asp:ListItem?Text="OR" RunAt="server" />
??????????????<asp:ListItem?Text="PA" RunAt="server" />
??????????????<asp:ListItem?Text="RI" RunAt="server" />
??????????????<asp:ListItem?Text="SC" RunAt="server" />
??????????????<asp:ListItem?Text="SD" RunAt="server" />
??????????????<asp:ListItem?Text="TN" RunAt="server" />
??????????????<asp:ListItem?Text="TX" RunAt="server" />
??????????????<asp:ListItem?Text="UT" RunAt="server" />
??????????????<asp:ListItem?Text="VA" RunAt="server" />
??????????????<asp:ListItem?Text="VT" RunAt="server" />
??????????????<asp:ListItem?Text="WA" RunAt="server" />
??????????????<asp:ListItem?Text="WI" RunAt="server" />
??????????????<asp:ListItem?Text="WV" RunAt="server" />
??????????????<asp:ListItem?Text="WY" RunAt="server" />
????????????</asp:DropDownList>
??????????</td>
??????????<td>
??????????</td>
????????</tr>
????????<tr>
??????????<td?align="right">
????????????Zip
??????????</td>
??????????<td>
????????????<asp:TextBox?ID="ZipCode" RunAt="server" />
??????????</td>
??????????<td>
????????????<asp:RegularExpressionValidator
??????????????ControlToValidate="ZipCode"
??????????????ValidationExpression="^(\d{5}│\d{5}\-\d{4})$"
??????????????ErrorMessage="Invalid?zip?code"
??????????????Display="dynamic"
??????????????RunAt="server"
????????????/>
??????????</td>
????????</tr>
????????<tr>
??????????<td?align="right">
????????????Credit?Card?Number
??????????</td>
??????????<td>
????????????<asp:TextBox?ID="CreditCardNumber" RunAt="server" />
??????????</td>
??????????<td>
????????????<asp:RegularExpressionValidator
??????????????ControlToValidate="CreditCardNumber"
??????????????ValidationExpression="^[\d\-]{15,20}$"
??????????????ErrorMessage="Invalid?card?number"
??????????????Display="dynamic"
??????????????RunAt="server"
????????????/>
??????????</td>
????????</tr>
????????<tr>
??????????<td>
??????????</td>
??????????<td>
????????????<asp:Button?Text="Sign?Me?Up" OnClick="OnSignMeUp"
??????????????RunAt="server" />
??????????</td>
??????????<td>
??????????</td>
????????</tr>
??????</table>
????</form>
??</body>
</html>

<script?language="C#" runat="server">
??void?OnSignMeUp?(Object?sender,?EventArgs?e)
??{
??????if?(IsValid)?{
??????????StringBuilder?sb?=?
??????????????new?StringBuilder?("Thanks.aspx?Name=",?256);
??????????sb.Append?(Name.Text);
??????????sb.Append?("&EMail=");
??????????sb.Append?(EMail.Text);

??????????string?address?=?Address.Text;
??????????string?city?=?City.Text;
??????????string?state?=?StateList.SelectedItem.Text;
??????????string?zip?=?ZipCode.Text;

??????????if?(address.Length?>?0?&&?city.Length?>?0?&&?
??????????????zip.Length?>?0)?{
??????????????sb.Append?("&Address=");
??????????????sb.Append?(address);
??????????????sb.Append?("&City=");
??????????????sb.Append?(city);
??????????????sb.Append?("&State=");
??????????????sb.Append?(state);
??????????????sb.Append?("&ZipCode=");
??????????????sb.Append?(zip);
??????????}

??????????string?number?=?CreditCardNumber.Text;

??????????if?(number.Length?>?0)?{
??????????????sb.Append?("&CreditCardNumber=");
??????????????sb.Append?(number);
??????????}

??????????Response.Redirect?(sb.ToString?());
??????}
??}
</script>
Figure 6-21
Source code for Spammers, Inc.
Thanks.aspx
<%@?Page?Language="C#" %>

<html>
??<body>
????Here's?the?information?you?entered:<br><br>
????<ul>
??????<%
????????Response.Write?("<li>Name: " +?Request["Name"]);
????????Response.Write?("<li>E-mail?address: " +?Request["EMail"]);

????????if?(Request["Address"]?!=?null)?{
????????????StringBuilder?sb?=?
????????????????new?StringBuilder?("<li>Address: ",?64);
????????????sb.Append?(Request["Address"]);
????????????sb.Append?(", ");
????????????sb.Append?(Request["City"]);
????????????sb.Append?(", ");
????????????sb.Append?(Request["State"]);
????????????sb.Append?(" ");
????????????sb.Append?(Request["ZipCode"]);
????????????Response.Write?(sb.ToString?());
????????}

????????if?(Request["CreditCardNumber"]?!=?null)
????????????Response.Write?("<li>Credit?card?number: " +
????????????????Request["CreditCardNumber"]);
??????%>
????</ul>
????Thanks?for?signing?up?with?Spammers,?Inc.!
??</body>
</html>
Connecting Web Forms with Response.Redirect

In addition to showing validators at work, Spammers, Inc., demonstrates another important technique for programming Web forms: how to transfer control from one page to another and transmit data in the process. The application contains not one, but two Web forms: SpammersInc.aspx and Thanks.aspx. When the user clicks the Sign Me Up button, SpammersInc.aspx uses Response.Redirect to transfer control to Thanks.aspx, which displays a summary of the user input along with a thank you (Figure 6-22).

How is the input transmitted to Thanks.aspx? SpammersInc.aspx appends it to the URL in the form of a query string. For example, if the user types “Jeff” in the name field and “jeffpro@wintellect.com” in the e-mail address field, Response.Redirect is passed the following URL:

Thanks.aspx?Name=Jeff&EMail=jeffpro@wintellect.com

Thanks.aspx extracts the data from the query string using the ASP.NET Request object and writes it to the HTTP response using Response.Write:

Response.Write?("<li>Name: " +?Request["Name"]);
Response.Write?("<li>E-mail?address: " +?Request["EMail"]);

The names used to retrieve data from the Response object must match the parameter names used in the query string. Code that relies on Request and Response objects looks more like ASP code than ASP.NET code, but this is nonetheless an efficient and extremely common technique for moving between pages in ASP.NET.

Figure 6-22
The Spammers, Inc., “thank you” page.
The StringBuilder Class

To formulate the URL passed to Response.Redirect, SpammersInc.aspx uses a StringBuilder object. StringBuilder is defined in the FCL’s System.Text namespace; it provides an efficient and easy-to-use mechanism for building strings on the fly.

Strings in managed applications are instances of System.String, but System.Strings are immutable, meaning once defined, they can’t be changed. The following code works but is inefficient because each concatenation operation results in a memory allocation and a memory copy:

string?s?= "";
for?(int?i=1;?i<=99;?i++)?{
????s?+=?i.ToString?();
????s?+= ", ";
}

The following code produces the same string, but it does so in a fraction of the time. Why? Because it builds the string in a buffer large enough to hold 512 characters:

StringBuilder?sb?=?new?StringBuilder?(512);
for?(int?i=1;?i<=99;?i++)?{
????sb.Append?(i.ToString?());
????sb.Append?(", ");
}
string?s?=?sb.ToString?();

StringBuilder.Append will enlarge the buffer if necessary, but if you know approximately how long the string will be before you start, you can size the buffer accordingly and keep new memory allocations to a minimum.

Building URLs with StringBuilder.Append is a performance optimization. Thanks.aspx also uses StringBuilder to build strings dynamically—not to build URLs, but to build the address that it outputs to the page. A few milliseconds per request might not seem like much, but if your application receives thousands of requests per second, a little can add up to a lot.

Conditional Validation

The vast majority of the time, input validation is unconditional. An e-mail address that needs validating one time needs it every time. But occasionally the need arises to validate conditionally—usually based on the state of something else on the page, such as a check box. For example, you might want to validate an e-mail address only if a check box labeled “E-mail my confirmation” is checked.

The Web page in Figures 6-23 and 6-24 demonstrates how to enact conditional validation. In this example, the user is invited to enter his or her name for the purpose of registering a product. The user can optionally enter an e-mail address and check a box to have a confirmation e-mailed. If the box is checked, the e-mail address is validated using a RequiredFieldValidator and a RegularExpressionValidator. If the box isn’t checked, validation is skipped—even if the user enters something into the e-mail field.

Notice the Enabled=“false” attributes in the tags that declare the validators. Enabled is a property that validators inherit from System.Web.UI.WebControls.WebControl. It defaults to true, meaning the validator will work as normal. Setting Enabled to false disables the validator and prevents it from performing its validation checks.

Now look at the <asp:CheckBox> tag. It sets AutoPostBack to true, forcing a postback to occur any time the check box is clicked. It also registers a handler—OnCheckBoxClicked—for the CheckedChanged event that fires each time the check box is clicked. OnCheckBoxClicked enables or disables the validators based on the state of the check box. It enables them if the box is checked and disables them if it’s not. Here’s the relevant code:

EMailRequiredValidator.Enabled?=?Confirm.Checked;
EMailExpressionValidator.Enabled?=?Confirm.Checked;

“EMailRequiredValidator” and “EMailExpressionValidator” are the validators’ programmatic IDs; “Confirm” is the CheckBox ID. By enabling the validators only when the check box is checked, ConditionalValidate.aspx prevents the validators from firing if the input from the control they’re attached to is meaningless.

Figure 6-23
The ConditionalValidate application.
ConditionalValidate.aspx
<html>
??<body>
????<h1>Conditional?Validation?Demo</h1>
????<hr>
????<form?runat="server">
??????<table?cellpadding="4">
????????<tr>
??????????<td?align="right">
????????????Name
??????????</td>
??????????<td>
????????????<asp:TextBox?ID="Name" RunAt="server" />
??????????</td>
??????????<td>
????????????<asp:RequiredFieldValidator
??????????????ControlToValidate="Name"
??????????????ErrorMessage="Please?enter?your?name"
??????????????Display="dynamic"
??????????????Color="red"
??????????????RunAt="server"
????????????/>
??????????</td>
????????</tr>
????????<tr>
??????????<td?align="right">
????????????E-Mail?Address
??????????</td>
??????????<td>
????????????<asp:TextBox?ID="EMail" RunAt="server" />
??????????</td>
??????????<td>
????????????<asp:RequiredFieldValidator
??????????????ControlToValidate="EMail"
??????????????ErrorMessage="Please?enter?your?e-mail?address"
??????????????Display="dynamic"
??????????????Color="red"
??????????????Enabled="false"
??????????????ID="EMailRequiredValidator"
??????????????RunAt="server"
????????????/>
????????????<asp:RegularExpressionValidator
??????????????ControlToValidate="EMail"
??????????????ValidationExpression="^[\w\.-]+@[\w-]+\.[\w\.-]+$"
??????????????ErrorMessage="Invalid?e-mail?address"
??????????????Display="dynamic"
??????????????Color="red"
??????????????Enabled="false"
??????????????ID="EMailExpressionValidator"
??????????????RunAt="server"
????????????/>
??????????</td>
????????</tr>
????????<tr>
??????????<td>
??????????</td>
??????????<td>
????????????<asp:CheckBox?ID="Confirm"
??????????????Text="E-mail?my?confirmation"
??????????????OnCheckedChanged="OnCheckBoxClicked"
??????????????AutoPostBack="true" RunAt="server" />
??????????</td>
??????????<td>
??????????</td>
????????</tr>
????????<tr>
??????????<td>
??????????</td>
??????????<td>
????????????<asp:Button?Text="Register" OnClick="OnRegister"
??????????????RunAt="server" />
??????????</td>
??????????<td>
??????????</td>
????????</tr>
??????</table>
??????<br><hr><br>
??????<asp:Label?ID="Output" RunAt="server" />
????</form>
??</body>
</html>

<script?language="C#" runat="server">
??void?OnCheckBoxClicked?(Object?sender,?EventArgs?e)
??{
??????EMailRequiredValidator.Enabled?=?Confirm.Checked;
??????EMailExpressionValidator.Enabled?=?Confirm.Checked;
??}
??void?OnRegister?(Object?sender,?EventArgs?e)
??{
??????if?(IsValid)?{
??????????if?(Confirm.Checked)?{
??????????????Output.Text?=
????????????????? "Confirmation?will?be?e-mailed?to " +
??????????????????EMail.Text?+ ".";
??????????}
??????????else?{
??????????????Output.Text?=
????????????????? "At?your?request,?no?confirmation?will " +
????????????????? "be?sent.";
??????????}
??????}
??}
</script>
Figure 6-24
Conditional input validation.