Introduction
ASP.NET provides different options for validation at the client side. Each of the validation controls emits client side JavaScript behind the scenes for data validation. In the earlier versions of ASP.NET, the client side library was open for modification. Starting with ASP.NET 2.0, these functions are in the source file WebUIValidation.js which is included as an embedded resource within the System.Web assembly. This article looks at these internal functions and takes a deep dive approach to understand how they work with web controls during the page validation process.
How does Validation actually work?
In order to demonstrate the ASP.NET validation process, let us create a simple example using these quick steps to create a web application called "SimpleValidationTest":
- Create a new WebSite in VS 2005.
- Drag and drop a textbox (
TextBox1
), a required field validator (RequiredFieldValidator1
), and a button (Button1
) on the form. - Add a validation summary (
ValidationSummary1
) control.
When an ASP.NET server side validation control is used, during the rendering phase it embeds in the HTML output some client side JavaScript code used to perform validation. All the ASP.NET validation controls inherit from BaseValidator
and the rendering of common functions happens within this class. When using any built in validation controls, these functions are always plugged into the rendered HTML source. The rendered functions are mentioned below along with a brief description.
Modified form tag: The first thing to note when using ASP.NET validation is that additional attributes are added to the form element. It is modified to execute the below JavaScript code for validation purposes.
- onsubmit="javascript:return WebForm_OnSubmit();"
The onsubmit
attribute is added during the rendering phase by the BaseValidator
class. Inside the OnPreRender
method, the common initialization tasks are invoked through the RegisterValidatorCommonScript
method. One of the steps is to render the submit script shown above, which is rendered via the Page.ClientScript.RegisterOnSubmitStatement
method.
Common JavaScript validation functions used across all validators are shown in listings 1 and 2 which can be seen in the rendered HTML source.
Listing 1: The WebForm_OnSubmit function
- function WebForm_OnSubmit() {
- if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false)
- return false;
- return true;
- }
The function WebForm_OnSubmit
in listing 1, which is invoked when the form is posted, does not submit the form if the page is not valid. This condition is determined by the ValidatorOnSubmit
function, shown in listing 2.
Listing 2: The ValidatorOnSubmit function
- var Page_ValidationActive = false;
- if (typeof(ValidatorOnLoad) == "function") {
- ValidatorOnLoad();
- }
- function ValidatorOnSubmit() {
- if (Page_ValidationActive) {
- return ValidatorCommonOnSubmit();
- }
- else {
- return true;
- }
- }
The ValidatorOnLoad
function enumerates through the validators collection on the page to initialize each of them, and sets Page_ValidationActive
to true
. In turn, the ValidatorOnSubmit
function is executed since Page_ValidationActive
is true
. By default, Page_BlockSubmit
is set to false
and ValidatorCommonOnSubmit
returns !Page_BlockSubmit
. During client side Page validation, Page_BlockSubmit
is set to !Page_IsValid
. Thus, an invalid page turns Page_IsValid
to false
, setting the return value of ValidatorCommonOnScript
to false
. The page validation process is explained in more depth in the section entitled "Page validation in depth".
The JavaScript snippet shown in listing 3 is specific to the particular validators (RequiredFieldValidator1
and ValidationSummary1
) used in the page.
Listing 3: Validator-specific code
- var Page_ValidationSummaries = new
- Array(document.getElementById("ValidationSummary1"));
- var Page_Validators = new Array(document.getElementById("RequiredFieldValidator1"));
- var RequiredFieldValidator1 = document.all ? document.all["RequiredFieldValidator1"]
- : document.getElementById("RequiredFieldValidator1");
- RequiredFieldValidator1.controltovalidate = "TextBox1";
- RequiredFieldValidator1.errormessage = "RequiredFieldValidator";
- RequiredFieldValidator1.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
- RequiredFieldValidator1.initialvalue = "";
The above code snippet shows that the validator names are added to the Page_Validators
collection and that the validation summary control names are added to the Page_ValidationSummaries
collection. The main attributes of the validator - namely controltovalidate
, errormessage
, evaluationfunction
and initialvalue
are initialized. The value of the evaluationfunction
property is determined by the type of the server side validator. The possible values are shown in the following table.
Table 1: Possible values for evaluationfunction rendered at client side
Validator type | evaluationfunction |
---|---|
Required Field Validator | RequiredFieldValidatorEvaluateIsValid |
Range Validator | RangeValidatorEvaluateIsValid |
Custom Validator | CustomValidatorEvaluateIsValid |
Compare Validator | CompareValidatorEvaluateIsValid |
Peeking into the .NET validation libraries
The validation functions listed above can be examined into much more detail if we can peek into WebUIValidation.js source file. There are different ways of achieving this and we are going to show a couple of approaches. The first approach is to peek into the System.Web.UI.WebControls assembly within the embedded resources. We do this with the help of the Reflector tool which uses .NET Reflection to generate the source code of any managed assembly. By opening .NET Reflector and navigating to System.Web, the JavaScript file WebUIValidation.js is found - located under the Resources folder. The second approach is to look at the source while debugging the code in the Visual Studio 2005 IDE. These steps would enable us to set breakpoints within the library and trace the code flow.
- Open the web application created earlier using Visual Studio 2005 (SimpleValidationTest example). Set the preferred default browser to Internet Explorer .This can be done by right clicking on the Default.aspx page and then clicking on Microsoft Internet Explorer in the browser window; and choosing the "Set as Default" option. Click on Debug->Start debugging or simply press F5 to run the application. Make sure that debugging is enabled in Internet Explorer by checking the Internet Options->Advanced tab under the Tools menu. The check box for "Disable script debugging" must be unchecked for enabling client side debugging.
- In the Visual Studio 2005 IDE, open the Script Explorer window located under Debug->Windows or under View->Other Windows on the menu. Alternatively, the shortcut Ctrl+Alt+N can be used to show the scripts being used for debugging. Inside the Script Exlorer window, all the JavaScript resources used in the application are displayed. Some of them are injected on the page by the ASP.NET 2.0 framework and others are user defined JavaScript functions. All the embedded resources such as images and scripts are rendered via the inbuilt HTTP handler WebResource.axd. The JavaScript file of our interest starts with the following line of code:
var
Page_ValidationVer
=
"125";
- Once the JavaScript file is opened, all the functions used internally by ASP.NET can be seen and break points can be placed to carry on the normal debugging process.
Page Validation in depth
Earlier, we noted that Page_IsValid
determines whether a given page is valid or not. But which function sets this value and how is the function invoked? Using our sample example, we determine the answer to this question by inspecting the HTML rendered by the button control. By default, for every button control, the following attribute is added:
Listing 4: Attribute added to every button controls
- onclick="javascript:WebForm_DoPostBackWithOptions(new
- WebForm_PostBackOptions"Button1", "", true, "", "", false, false))"
The onclick
attribute, along with the script, is added inside the AddAttributesToRender
method of the Button
control. The WebForm_DoPostBackWithOptions
function can be examined in the rendered web resource JavaScript file within Visual Studio 2005, as explained in the above section. This function is in the resource file named WebForms.js. The function takes an argument of type WebForm_PostBackOptions
. This function is also responsible for invoking the Page_ClientValidate
function, which sets the value of the Page_IsValid
member. The complete validation process triggered by the click of a button process can be summarized in the workflow below.
- Button is clicked
- function in listing 7 invoked
- function in listing 5 invoked
Page_IsValid
is set- form is submitted
onSubmit
event is raised- return
WebForm_OnSubmit
- return
ValidatorOnSubmit
- return
ValidatorCommonOnSubmit
- return
!Page_BlockSubmit
- return
!(!Page_IsValid)
The above workflow returns true
if the page is valid, which in turn submits the form. It returns false
if the page is invalid, preventing the form from being submitted. In the latter case, the form displays the error messages under the validation summary control, due to the functions ValidatorUpdateIsValid
and ValidationSummaryOnSubmit
as shown in the first function reported in Listing 5.
These functions (Page_ClientValidate
and WebForm_DoPostBackWithOptions
), along with the definition of WebForm_PostBackOptions
as seen on Visual Studio 2005, are shown in listings 5, 6, and 7.
Listing 5: The Page_ClientValidate function
- function Page_ClientValidate(validationGroup) {
- Page_InvalidControlToBeFocused = null;
- if (typeof(Page_Validators) == "undefined") {
- return true;
- }
- var i;
- for (i = 0; i < Page_Validators.length; i++) {
- ValidatorValidate(Page_Validators[i], validationGroup, null);
- }
- ValidatorUpdateIsValid();
- ValidationSummaryOnSubmit(validationGroup);
- Page_BlockSubmit = !Page_IsValid;
- return Page_IsValid;
- }
Listing 6: The WebForm_PostBackOptions function
- function WebForm_PostBackOptions(eventTarget, eventArgument, validation, validationGroup, actionUrl, trackFocus, clientSubmit) {
- this.eventTarget = eventTarget;
- this.eventArgument = eventArgument;
- this.validation = validation;
- this.validationGroup = validationGroup;
- this.actionUrl = actionUrl;
- this.trackFocus = trackFocus;
- this.clientSubmit = clientSubmit;
- }
Listing 7: The WebForm_DoPostBackWithOptions function
- function WebForm_DoPostBackWithOptions(options) {
- var validationResult = true;
- if (options.validation) {
- if (typeof(Page_ClientValidate) == 'function') {
- validationResult = Page_ClientValidate(options.validationGroup);
- }
- }
- if (validationResult) {
- if ((typeof(options.actionUrl) != "undefined") &&
- (options.actionUrl != null) &&
- (options.actionUrl.length > 0))
- {
- theForm.action = options.actionUrl;
- }
- if (options.trackFocus) {
- var lastFocus = theForm.elements["__LASTFOCUS"];
- if ((typeof(lastFocus) != "undefined") && (lastFocus != null)) {
- if (typeof(document.activeElement) == "undefined") {
- lastFocus.value = options.eventTarget;
- }
- else {
- var active = document.activeElement;
- if ((typeof(active) != "undefined") && (active != null)) {
- if ((typeof(active.id) != "undefined") &&
- (active.id != null) &&
- (active.id.length > 0))
- {
- lastFocus.value = active.id;
- }
- else if (typeof(active.name) != "undefined") {
- lastFocus.value = active.name;
- }
- }
- }
- }
- }
- }
- if (options.clientSubmit) {
- __doPostBack(options.eventTarget, options.eventArgument);
- }
- }
Using built-in validation routines in user code
In the previous sections, we looked at the internals of the page validation process. In the following example, we are going to show a sample ASP.NET page where validation is turned on or off depending on a condition. Let us add another TextBox and custom validator to our sample application. This validation function determines whether the required field validator is enabled or not. The validateTextBox1
function takes in two arguments, src
and args
. When the value entered in TextBox2
is true
, RequiredFieldValidator1
is programmatically disabled. This behavior is achieved by leveraging the built-in ValidatorEnable
function, which takes in two arguments, the name of the validator and a Boolean flag to enable/disable it. The errormessage
attribute of the custom validator is dynamically changed within the code, depending on the user entry. The form is prevented from being submitted by setting args.IsValid
to false
. The complete source code is shown below.
Listing 8: Source code for the ASP.NET page
- <html xmlns="http://www.w3.org/1999/xhtml" >
- <head runat="server">
- <title>Understanding .NET Validation</title>
- <script type="text/javascript">
- function validateTextBox1(src, args)
- {
- if (args.Value == "true")
- {
- ValidatorEnable(RequiredFieldValidator1, false);
- src.errormessage = "TextBox1 required field validator is disabled";
- }
- else
- {
- ValidatorEnable(RequiredFieldValidator1, true);
- src.errormessage = "Enter true, not '"+args.Value+"' to disable TextBox1 required field validator";
- }
- args.IsValid = false;
- }
- </script>
- </head>
- <body>
- <form id="form1" runat="server">
- <div>
- TextBox 1: <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
- <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server"
- Text="*" ControlToValidate="TextBox1"
- Display="Dynamic"
- ErrorMessage="TextBox1 is required"></asp:RequiredFieldValidator>
- <br />
- Disable required field for TextBox1?
- <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
- <asp:CustomValidator ID="CustomValidator1" runat="server"
- Text="*" ControlToValidate="TextBox2" Display="Dynamic"
- ErrorMessage="CustomValidator"
- ClientValidationFunction="validateTextBox1"
- ValidateEmptyText="True"></asp:CustomValidator>
- <asp:ValidationSummary ID="ValidationSummary1" runat="server" />
- <asp:Button ID="Button1" runat="server" Text="Button" />
- </div>
- </form>
- </body>
- </html>
Conclusion
The article explained the steps involved in validating and submitting a form in ASP.NET 2.0. Also, the main functions within the framework involved in this process were identified and addressed briefly. ASP.NET 2.0 has a rich client side framework, and understanding the internal implementation helps us to leverage and customize its behavior.