Tuesday, March 22, 2005

[itsdifferent] ASP.NET Validation in Depth

---------- Forwarded message ----------
From: Deven Goratela
Date: Fri, 18 Mar 2005 12:34:08 +0530
Subject: [itsdifferent] ASP.NET Validation in Depth
To: itsdifferent@yahoogroups.com

Introduction

This article discusses in detail how the ASP.NET validation controls
work. This article is recommended reading for anyone building complex
pages with validation controls or those looking to extend the
validation framework. For people looking to get started with the
validation controls or deciding whether to use them, see User Input
Validation in ASP.NET.
In the Beginning

Throughout the development of ASP.NET, we knew that helping out with
validation was important. Take a look at most commercial Web sites
today and you will notice that they are filled with forms that clearly
execute a lot of handwritten code to perform validation. Validation
code is not particularly sexy to write. It can be exciting to write
code to display tables of data or to dynamically generate charts, but
no one gets their coworkers to check out the cool way they stopped
someone from entering a blank value for a name field.

Validation of Web applications is particularly frustrating for other
reasons as well. HTML 3.2 is so limited in what you can control and
what feedback you get from the user that you can't apply the same
tricks you can use on a richer client, such as preventing the user
from entering certain characters, or making beep sounds. It is
possible to create more powerful validation using browser script. This
can be hard to justify, however, because script is not always present
in client browsers and can be bypassed by malicious users. It is
necessary, therefore, to implement the same checks on the server
anyway, in order to have a secure site.

In the development of ASP.NET, our original intention was to have just
one control to handle validation, which would have been a version of
the TextBox control that could also display errors. When the time came
to design the control, however, it became clear that this would not
cut the mustard. We looked at a large number of data-entry forms and
tried to find a solution that would fit as many of them as possible.
We found a number of interesting things about data-entry forms:

· It is common to have error messages or icons
adjacent to input elements, but they are almost always positioned in
different table cells.

· It is common to have an area of the page where
all errors are summarized.

· Many sites contain client-side script to provide
more immediate feedback and prevent wasted round-trips to the server.

· Many sites that have client-side script display
message boxes if there were errors.

· It is not just text inputs that are validated.
Drop-down lists and radio buttons are also frequently validated.

· When a field is empty, sites generally display a
message or icon that is different from the one they display when the
entry is invalid.

· Many validation checks are excellent candidates
for regular expressions.

· It is common for validity to be dependent on
comparisons between inputs.

· Among validation tasks, 90 percent or more are
common operations like checking names and ZIP codes. Most sites seemed
to be reinventing the wheel.

· There is generally too much variation between
sites to have a perfect solution that can do 100 percent of validation
tasks for every site.

Consideration of all these points led to the eventual solution of the
five Validator controls, the ValidationSummary control, and
integration with the Page object. It was also clear that the solution
needed to be extensible, and there needed to be an API for working
with it on both the client and the server.

When we looked at the different sorts of validation that takes place,
it seemed like we would need a bigger toolbox. In most component
environments like Microsoft(r) ActiveX(r), we probably would have tried to
overload the functionality of all the validation controls into one
control that worked with different properties in different modes.
However, thanks to the magic of inheritance in the Microsoft(r) .NET
Framework, it is possible to provide a suite of controls that do
specific validation with specific properties, because the overhead of
deriving each new control is very small.

Most of the work done by these controls is implemented in their common
parent, BaseValidator. You can also derive from BaseValidator or the
other controls to take advantage of this. In fact, even BaseValidator
is too lazy to implement its own Text property and inherits from
Label.
What Happens When?

It is beneficial to understand the sequence of events when a page with
validation Web controls is processed. If any of the validation
conditions are optional, you will want to know exactly when validation
takes place on both the client and the server. If you are writing your
own validation routines that are potentially time-consuming or that
have side effects, it is also important to have an idea of when they
will be called.

First, let's look at the server.
Server-Side Validation Sequence

It is important to understand the life cycle of a page. For those used
to working with forms in Visual Basic or similar rich client tools, it
takes a bit of getting used to. A page and all the objects on it do
not actually live for as long as a user is interacting with them,
although it can sometimes seem like they do.

Here is a simplified sequence of events when a page is first accessed:

1. Page and its controls are created, based on ASPX file.

2. Page_Load event fires.

3. Page and control properties are saved to a hidden field.

4. Page and controls are turned into HTML.

5. Everything is thrown away.

Now, when a user clicks on a button or similar control, it goes back
to the server and does a similar sequence. This is called the
post-back sequence:

1. Page and its controls are created based on ASPX file.

2. Page and control properties are recovered from
hidden field.

3. Page controls are updated based on user input.

4. Page_Load event fires.

5. Change notification events fire.

6. Page and control properties are saved to a hidden field.

7. Page and controls are turned into HTML.

8. Everything is thrown away again.

Why don't we just keep all objects in memory? Because Web sites build
with ASP.NET would not work with very large numbers of users. This
way, the only objects in memory on the server are things being
processed right now.

When does server-side validation take place? Well, it does not take
place at all on the first page fetch. Most of our end-users are very
diligent, and we want to give them the benefit of the doubt that they
will fill in the form correctly before we bombard them with red text.

On the post-back, validation takes place during step 5, just before
the event fires for the button or control that triggered the
validation. Button controls in ASP.NET have a property called
CausesValidation that defaults to True. It is the action of clicking
on buttons that makes validation happen. The best place to check the
results of validation is in the event handler that triggered the
validation. You can also have buttons with CausesValidation=False,
that will not cause any validators to be evaluated.

One potentially confusing thing about this timing is that the
validators will not have been evaluated at the time Page_Load is
triggered. The benefit of this is that it gives you a chance to
programmatically change property values affecting the validity of the
page, such as enabling or disabling certain validators.

If this timing is not to your liking and you prefer to evaluate
everything in Page_Load, you can do this by explicitly triggering the
validation during this event by calling Page.Validate. After this has
been called you can then check the result of Page.IsValid. If you try
to query the result of Page.IsValid before Page.Validate has been
called, either explicitly or being triggered by a button with
CausesValidation=True, then its value is meaningless, so an exception
will be thrown.
The Page API

The Page object has some important properties and methods with respect
to server-side validation. They are summarized in Table 1:

Table 1. Page object properties and methods

Property or Method

Description

IsValid property

This is the most useful property. It allows you to check whether the
entire form is valid. This is generally done before performing a
database update. It is true only if all the objects in the Validators
collection are valid, and it does not cache this value.

Validators property

A collection of all the validation objects for the page. It is a
collection of objects that implement the IValidator interface.

Validate method

A method that is called at validation time. The default implementation
on Page goes to each validator and asks it to evaluate itself.

The Validators collection is useful for a number of things. It is a
collection of objects of that implement the IValidator interface. I
use the term objects rather than controls because the Page cares only
about the IValidator interface. While it happens that all the
validators will generally be visual controls that implement
IValidator, there is no reason someone cannot come along with an
arbitrary validation object and add it to the page.

The IValidator interface has the following properties and methods:

Table 2. IValidator interface properties and methods

Property or Method

Description

IsValid property

Indicates whether or not the validation check made by the individual
validating object has passed. You can manually alter this value after
validation has taken place.

ErrorMessage property

Describes the error that the validation object is validating and that
may be displayed to the user.

Validate method

Executes the validation check of the validation object to update its
IsValid value.

You can do some interesting things with this interface. For example,
to reset the page to a valid state, use the following code (examples
shown in C#): IValidator val; foreach(val in Validators) {
Val.IsValid = true; }

To re-execute the whole validation sequence, use the following code:
IValidator val; foreach(val in Validators) {
Val.Validate(); }

This is equivalent to calling the Validate() method on Page.

Another way to make some changes before validation takes place is to
override the Validate method. This example shows a page that contains
a validator that is turned on or off based on the value of a checkbox:
public class Conditional : Page { public HtmlInputCheckBox
chkSameAs; public RequiredFieldValidator rfvalShipAddress;
public override void Validate() { // Only check ship
address if not same as billing bool enableShip =
!chkSameAs.Checked; rfvalShipAddress.Enabled = enableShip;
// Now perform validation base.Validate();
} }
Client-Side Validation

If client-side validation is enabled for your page, a whole different
sequence occurs in-between the round trips. Client-side validation
works using client JScript(r). No binary components are needed to make
it work.

While the JScript language is reasonably well standardized, the
Document Object Model (DOM) for interacting with HTML documents in
browsers did not have a widely adopted standard at the time these
components were developed and tested. As a result, client-side
validation only takes place in Internet Explorer 4.0 and later,
because it targets the Internet Explorer DOM.

From a server point of view, client-side validation just means that
the validation controls emit different stuff into the HTML. Other than
that, their sequence of events is exactly the same. The server-side
checks are still carried out. This may seem redundant, but it is
important because of the following:

· Some validation controls may not support client
scripting. A good example: if you are using a CustomValidator with a
server validation function but no client validation function.

· Security considerations. Someone can very easily
take a page with script and disable or change it. You should not rely
on script to stop bad data getting into to your system, only to
provide more immediate feedback to your users. For this reason, if you
are using a CustomValidator, you should not provide a client
validation function without a corresponding server validation
function.

Every validation control makes sure that a standard block of client
script is emitted into the page. This is actually just a small amount
of script that includes a reference to code in a script library called
WebUIValidation.js. This file, which is downloaded separately and can
be cached by the browser, contains all of logic for client-side
validation.
About the Script Library

Because the validation Web controls script that is in a script
library, it is not necessary to emit all of the code for client-side
validation directly into the page, although it acts as though this is
what has happened. The main script file reference looks like
this:

By default, the script file will be installed into your default root
in the aspnet_client directory, and it is referenced using a
root-relative script include directive, which begins with the forward
slash. This reference means that each individual project does not have
to include the script library inside it, and all pages on the same
machine can reference the same file. You will notice that it also has
the common language runtime version number in the path, so that
different versions of the runtime can run on the same machine.

If you take a look around your default virtual root, you can find this
file and take a look inside it. The location of these files is
specified in the machine.config file, an XML file used for most
ASP.NET settings. Here is the definition of the location within that
file: clientScriptsLocation="/aspnet_client/{0}/{1}/" />

You are encouraged to read the script to see more of what is going on.
However, it is not recommended that you modify these scripts, because
their function is very closely tied to a particular version of the run
time. If the run time is updated, the scripts may need a corresponding
update, and you will have to either lose your changes or face problems
with the scripts not working. If you must change the scripts for a
particular project, take a copy of the files and point your project to
them by overriding the location of the files with a private web.config
file.It is perfectly fine to change this location to be a relative or
absolute reference.
Disabling Client-Side Validation

There are some cases where you may not want client-side validation. If
the number of input fields is very small, client-side validation may
not be of much benefit. You may have logic that needs a round trip to
the server every time anyway. You may find that the dynamically
appearing messages on the client have an adverse affect on your
layout.

Note The way to disable client-side validation is to set the
EnableClientScript property of the validator or ValidationSummary
control to False. It is possible to have a mixture of server-only and
client-sever validation components on the same page.
The Client-Side Sequence

This is the sequence of events when a page with client-side validation runs:

1. As the page is loaded into the browser, there is
some initialization done on each validation control. The controls are
emitted as tags with HTML attributes that correspond closely to
their properties on the server. The most important thing that happens
here is that any input elements referenced by the validators are
"hooked up." The referenced input elements have their client events
modified so that the validation routines are called whenever the input
is changed.

2. Code in the script library will be executed as
users tab from field to field. The validation conditions are
re-evaluated when a dependent field is changed, and the validator is
made visible or invisible as appropriate.

3. When the user pushes a button that has the
CausesValidation property set to True, the validators are all
re-evaluated. If they are all valid, then the form is posted to the
server. If there are one or more errors, a number of things happen:

· The submit is cancelled. The form
does not get posted back to the server.

· Any invalid validators become visible.

· If there is a validation summary with
ShowSummary=true, it will collect all the errors from the validation
controls and update its contents with them.

· If there is a validation summary with
ShowMessageBox=true, it will collect the errors and display them in
client message box.

Because they are executed whenever the inputs change as well as at
submit time, client side validation controls generally evaluate two or
more times on client. Remember that they will still be re-evaluated on
the server once the submit takes place.
The Client-Side API

There is a mini-API that you can use on the client to achieve various
effects with your own client-side code. Because it is not possible to
make certain routines hidden, you can theoretically make use of any of
the variables, attributes, and functions defined by client-side
validation script. However, many of them are implementation details
that may be changed. Here is a summary of the client-side objects that
we encourage you to use.

Table 3. Client-side objects

Name

Type

Description

Page_IsValid

Boolean variable

Indicates whether the page is currently valid. The validation scripts
keep this up to date at all times.

Page_Validators

Array of elements

This is an array containing all of the validators on the page.

Page_ValidationActive

Boolean variable

Indicates whether validation should take place. Set this variable to
False to turn off validation programmatically.

isvalid

Boolean property

This is a property on each client validator indicating whether it is
currently valid.
Bypassing Client Validation

A common task you may need to do is to have a "Cancel" button or a
navigation button on a page. In this case, set the CausesValidation
property on the button to False and no validation will take place,
either on the server or the client. If you lay out a page like this,
you will want to check Page.IsValid in your button even handlers. If
you instead call Page.Validate during Page_Load, you will not have a
way of knowing whether a submit or cancel button was pushed.
Special Effects

Another common requirement is to have effects other than the error
messages displayed by the validators themselves in error situations.
In this case, any modifications in behavior you make need to be made
on both the server and the client. Suppose you want to have a Label
that changes color depending on whether an input is valid. Here is how
you would do this on the server:public class ChangeColorPage : Page {
public Label lblZip; public RegularExpressionValidator valZip;
protected override void OnLoad(EventArgs e) { Page.Validate();
lblZip.ForeColor = valZip.IsValid? Color.Black :
Color.Red; } }

This is all very nice, but whenever you modify validation like this,
you may find that it looks inconsistent unless you do an equivalent
operation on the client. The validation frameworks saves you from a
lot of this double effort, but for extra effects you just have to do
it in two places. Here is a client fragment that does the same
thing:
/>
runat=server ControlToValidate=txtZip ErrorMessage="Invalid Zip
Code" ValidationExpression="[0-9]{5}" />
language=javascript>function txtZipOnChange() { // Do nothing if
client validation is not active if (typeof(Page_Validators) ==
"undefined") return; // Change the color of the label
lblZip.style.color = valZip.isvalid ? "Black" : "Red";}
Client-Side APIs

Some additional scenarios are enabled by functions that can be called
from your client-side script.

Table 4. Functions called from client-side script

Name

Description

ValidatorValidate(val)

Takes a client-validator as input. Makes the validator check its input
and update its display.

ValidatorEnable(val, enable)

Takes a client-validator and a Boolean value. Enables or disables a
client validator. Being disabled will stop it from evaluating and it
will always appear valid.

ValidatorHookupControl(control, val)

Takes an input HTML element and a client-validator. Modifies or
creates the element's change event so that it updates the validator
when changed. This can be useful for custom validators that depend on
multiple input values.

Of particular use is to be able to enable or disable validators. If
you have validation that you want active only in certain scenarios,
you may need to change the activation on both server and client, or
you will find that the user cannot submit the page.

Here is the previous example with a field that should only be
validated when a check box is unchecked: public class Conditional :
Page { public HtmlInputCheckBox chkSameAs; public
RequiredFieldValidator rfvalShipAddress; public override void
Validate() { bool enableShip = !chkSameAs.Checked;
rfvalShipAddress.Enabled = enableShip; base.Validate();
} }

Here is the client-side equivalent:id=chkSameAs onclick="OnChangeSameAs();" >Same as
Billing

Validity Rules and Meaningful Error Messages

Each validator displays a specific error message about a specific
condition on a specific control. There are rules as to what is
considered valid that may at first seem confusing to you as a
developer, but they are necessary to allow you to construct error
messages that are actually helpful to the user.

All of the validators (except for RequiredFieldValidator) are
considered valid if they are blank. If a blank value is not valid, you
generally need to provide a RequiredFieldValidator in addition to
another validator. You need to do this because almost universally you
want different error messages for the blankness and for the validity.
Otherwise, you end up with confusing messages like "You must enter a
value and it must be between 1 and 10."

Another special rule relates to CompareValidator and RangeValidator
when the input fields cannot be converted to the specified data type.
The evaluation of validity for the CompareValidator with
ControlToCompare specified goes like this:

1. If the input field referenced by ControlToValidate
is blank, it is valid.

2. If the input field referenced by ControlToValidate
cannot be converted to the data type, it is invalid.

3. If the input field referenced by ControlToCompare
cannot be converted to the data type, it is valid.

4. The inputs fields are converted to the data type
and compared.

The third step may seem a little counterintuitive. The step works this
way because it would be hard to write a meaningful error message for
the validator if it were checking the validity of more than one field
at a time. A separate validator should be used to report on error
conditions in the ControlToCompare input field. RangeValidator works
in a similar way with its maximum and minimum properties.
Effect of Enabled, Visible, and Display Properties

The difference between the Enabled, Visible, and Display properties on
validators may not be immediately obvious.

Display=None can be used to specify a validator that does not display
anything directly, but still gets evaluated, still affects overall
validity, and can still put an error in the summary on both client and
server. For client-side validation, these values determine whether the
visibility or the display style attributes are used to turn the
validator on or off. For server-side validation, Display=Dynamic means
that nothing at all displays when the input is valid, while
Display=Static means that a single nonbreaking space (" ") is
emitted. This last behavior exists so that table cells containing only
validators do not collapse to nothing when valid.

Why not just use Visible=false to have an invisible validator? In
ASP.NET the Visible property of a control has a very strong meaning: a
control with Visible=false will not be processed at all for
pre-rendering or rendering. As a result of this stronger meaning,
Visible=false for a validator means that not only does not it not
display anything, it is does not function either. It is not evaluated,
does not affect page validity, and does not put errors in the summary.

Enabled treads middle ground here. For the most part, Enabled=false
has the exact same effect as Visible=false. However, in client-side
validation, a disabled validator is still sent to the browser, but in
a disabled state. You can activate it with the ValidatorEnable
function in client script.

When using Visible or Enabled to control whether validation takes
place, bear in mind the sequence of events on the server above. Either
change them before validation takes place, or re-validate afterwards.
Otherwise, their IsValid values may not reflect the changes to their
properties.
The CustomValidator Control

The easiest way to extend the validation framework is to use the
CustomValidator control. This can be used either to perform validation
that is not covered by something another validation control can do or
to perform validation that requires access to information on the
server, such as a database or Web service.

If you add a CustomValidator with just a server validation function
defined, you will notice that it does not take part in client-side
validation. The CustomValidator is not updated as users tab between
fields, and it requires a round trip to the server to perform its
validation. If you are using a CustomValidator to perform a check that
does not need any information that lives on the server, you can also
have your validator fully participate in client-side validation by
using the ClientValidationFunction property. It is assumed that if you
provide a ClientValidationFunction, it should ideally perform exactly
the same checks as your server validation handler. Failing that, it
should perform a subset of that verification. Don't have a client
validation function that does more verifications than are performed on
the server, as hackers will be able to bypass it easily.

Here is a simple example of a CustomValidator that works on the client
and the server that just checks to see that the input is an even
number. First, here is the server function (in C#):protected void
ServerValidate(object source, ServerValidateEventArgs args) { try {
int i = Int32.Parse(args.Value); args.IsValid = ((i %
2) == 0); } catch { args.IsValid = false; } }

Here is how it is declared on the client, along with a client
validation function that performs the same check. This would usually
be in JScript, although it can also be VBScript(r) if you are targeting
Microsoft(r) Internet Explorer.id="CustomValidator1" runat="server" ErrorMessage="Number not
divisible by 2!" ControlToValidate="txtCustomData"
OnServerValidate="ServerValidate"
ClientValidationFunction="CheckEven" />
Input:id="txtCustomData" runat="server" />language="javascript">

Here are some points to note about using CustomValidator:

· Like all the other validation controls (besides
RequiredFieldValidator), it is considered valid if the input field is
blank.

· For older browsers or when client validation is
turned off, your client validation function will not get called. You
don't have to check the browser capabilities yourself before defining
the function, but you do need to make sure it does not cause script
errors just by being defined. Always enclose your client code in an
HTML comment, as in the example.

· Two parameters are passed into your client
function, corresponding to the parameters that are passed to the
server function. The first is the client validator element, and the
second is the equivalent of the arguments on the server. It has two
properties, the Value, which contains the input to be validated and
IsValid, which you can update to indicate validity.

· You can leave the ControlToValidate blank. In
this mode, the server function always fires once per round trip and
the client function always fires once for each attempt to submit. You
can use this to validate controls that cannot otherwise be validated,
such as a CheckBoxList or stand-alone radio buttons. It can also be
useful when the condition is based on multiple controls and you don't
want it evaluated as the user tabs between fields on the page.

· You can also hook up the change events of more
than one control. You can do this by having some inline script that
calls the client function ValidatorHookupControl, described above.
Which Controls Can Be Validated?

In order to be referenced by a validation control, a control must have
a validation property. All controls that can be validated have a
ValidationPropertyAttribute, which indicates which property should be
read for the purposes of validation. If you write your own control,
you can make it take part in validation by providing one of these
attributes to specify which property to use.

For validation to work client-side as well, this property must
correspond to the value attribute of the HTML element that gets
rendered on the client. Many complex controls such as DataGrid and
Calendar do not have a value on the client and can only be validated
on the server. This is why only controls that correspond closely to
HTML elements can be involved in validation. Also, a control must have
a single logical value on the client. This is why RadioButtonList can
be validated, but CheckBoxList cannot.

Regards,

Deven Goratela

Note: This Group is not a Job Searching Group, so please co-operate
and dont transfer any kind of job related material across this
Group.AnyOne doing so can be banned from the Group
Thanx , Group Co-Ordinators

Yahoo! Groups Sponsor

--
--
Regards...
/*******************************
itsDifferent
http://its-Different.blogspot.com
http://groups.yahoo.com/group/itsdifferent/join
http://groups.msn.com/its-Different/join
http://groups-beta.google.com/group/itsdifferent
*******************************/

No comments: