Implementing Client-Side Validation

Jennifer Ball

Problem Description

Most web applications include form elements that allow a user to enter values or choose items from a list. A web application must validate these values and choices to protect its business objects from invalid data, which is often used by malicious individuals to gain control of the system on which the web application is running.

Developers can employ different techniques for including validation code in web applications. These techniques include ways to perform validation on the server, on the client, or both. Server-side validation and client-side validation each have their advantages and drawbacks.

Performing validation on the client reduces network traffic and the load on the server, which often results in a faster response time and ultimately a more pleasant experience for the user. However, not all clients support the client-side scripts that must perform the validation. Also, different clients might support different scripting languages. Consequently, developers at the very least need to provide server-side validation whether or not they include support for client-side validation in their applications.

As opposed to client-side validation, server-side validation requires extra round-trips to the server, resulting in a slower response time on even the simplest validation checks. However, a developer doesn't need to worry about supporting a growing number of clients when they perform validation exclusively on the server.

A developer who chooses to provide both client-side and server-side validation will be stuck maintaining two redundant pieces of code.

Developers can maximize the advantages of client-side and server-side validation while minimizing their drawbacks by generating the client-side script from the same set of rules that apply to the server-side validation code. In fact, this strategy might be required as in the situation where the client-side script needs to access some information on the server to determine the validity of the input.

Solution

Among the many powerful features  of JavaServer[TM] Faces is its validation model, which makes it easy to include basic server-side validation in your application and provides a flexible programming model for implementing your own custom solution.

JavaServer Faces doesn't yet include support for client-side validation. However, the JavaServer Faces component rendering model allows you to create renderers that can generate client-side validation code targeted to various clients.

While this can be a big advantage for applications that can run on different clients, this strategy requires the developer to maintain two redundant pieces of code: one for server-side validation and one for client-side validation.

One solution for applications that will be rendered only in HTML is to use Struts Validator while still using the other features included with JavaServer Faces technology. Struts Validator not only includes a richer set of standard validators, but these validators perform both server-side and client-side validation. Struts Validator also allows you to plug in custom validators by adding them to the centralized validator-rules.xml file, which includes the corresponding client-side JavaScript[TM] for the validator. The centralized rules file also makes it easier to maintain the client-side and server-side code.

Considering all of this information, consider the following recommendations:

This document provides more details regarding strategies for performing client-side validation using JavaServer Faces technology by itself and in combination with Struts.

Adding Client-Side Scripting to a JavaServer[TM] Faces Page

JavaServer Faces technology provides default components that can be rendered to HTML clients. This means that you can associate an HTML "onsubmit" attribute with a JavaServer Faces HTML form component. Keep in mind that JavaServer Faces attribute names are case sensitive and must use all lower case characters in this attribute name.

Here is an example JavaScript tag included in a JavaServer Faces page:



<script language="JavaScript">
function client_validation() {
if (document.forms[0].elements["validatorForm:color"].value =="red")
return true;
} else if (document.forms[0].elements["validatorForm:color"].value =="green") {
return true;
} else if (document.forms[0].elements["validatorForm:color"].value =="blue") {
return true;
} else {
alert("Enter a red, green, or blue for the color.");
return false;
}
}
</script>


<h:form id="validatorForm" onsubmit="return client_validation();">
...
<p>Enter a color (red,green, or blue):</p>
<h:inputText id="color" value="#{ValidatorBean.color}"/>
<p>
<h:commandButton id="submit" action="success" value="Submit" />
</p>
</h:form>


This form shows validation using JavaScript. Note that the id's of the form elements checked in the JavaScript are named using [form name]:[form element id]. In the case of this example, you will reference the color input element using validatorForm:color as the name. You will not be able to access the form elements from JavaScript using a named approach, such as with window.document.validatorForm.validatorForm:color.value == "red" ), because there is a colon used in the id of the form element.

Notice that the JavaScript code is hardcoded into the page. This strategy is acceptable if the JavaScript code is only used on one page or a small set of pages, and does not require any data or processing from the server.

If the script is used on multiple pages, it makes more sense to keep the script on a separate page (such as clientValidation.js ) and include it in the pages using a specialized version of the script tag:

<script language="Javascript" type="text/javascript" src="clientValidation.js"/>

However, this solution doesn't solve the problem of how the script gets information from the server if it requires it. For these and many other reasons, it's often a better idea to generate the client-side script, as described in the next section.

Generating JavaScript[TM] Using a Custom Renderer

If a client-side script needs some data from the server to perform its function, you need to include the data in the script while generating it on the server. By generating the script, you also avoid having to manually copy the script to other pages that might need it.

Another reason to generate the script is if you want various clients (including those that don't support JavaScript) to be able to run this application. JavaServer Faces technology offers a rich component rendering model, which allows you to create custom renderers that can be used to render components in different ways and to different clients.

The component classes themselves encapsulate the functionality of the component. For example, the standard UISelectOne component allows a user to select one of a set of values. A renderer can define how this component should appear on the page. A UISelectOne component can be rendered as a set of radio buttons, a menu, a listbox, or something else by applying the appropriate renderer to the component. The page author chooses the component and renderer combination by using the tag that represents it on the page. The standard tag selectOneRadio represents the combination of a UISelectOne component and a Radio renderer, which renders a set of radio buttons.

You can also use a custom renderer to render client-side script, allowing you to generate the script on the server-side and therefore give the script access to server-side data. Add to this the fact that you can write separate renderers, each of which renders script appropriate for a particular client, and you have a much more flexible and portable way of incorporating client-side scripting in your applicaitons.

In the case of generating client-side script, it's not necessary to create a custom component in addition to a custom renderer. You can use one of the standard components instead. To create a custom renderer with a component you need to perform the following tasks:

To demonstrate how to render script with a custom renderer, consider the example from the previous section. This time, instead of hardcoding the script in the page, we'll write a custom renderer that generates it.

Choosing a Component

To render a custom tag from a custom renderer, you must pair the renderer with either a standard or a custom component. For this example that merely renders a script tag within a form, the simple component, UIOutput, will work. If you are going to add client-side script to an individual form component, you must either make this component a custom component for which you can create a custom renderer, or you must require the page author to add the script to the corresponding component tag in the page. For example, a page author can add an onselect attribute that has some JavaScript (or references the script using the JavaServer Faces EL) to the tag representing a checkbox component.

Creating the Renderer Class

A renderer class typically includes encoding methods that produce the markup. The encodeEnd method from ColorRenderer is shown below. The JavaServer Faces implementation calls the encodeEnd method with the component that it is trying to render, which is the UIOutput component that we are associating with this renderer. As shown in the encodeEnd method, this renderer writes out a script that does the same thing as the script we hardcoded in the page in. See the section above, "Adding Client-Side Scripting to a JavaServer Faces Page."

public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {

if ((context == null) || (component == null)) {
throw new NullPointerException();
}
UIOutput script = (UIOutput) component;
UIInput currentColor = (UIInput) component.findComponent("color");
ResponseWriter writer = context.getResponseWriter();
StringBuffer sb = null;

System.out.println("currentColor =" + currentColor);
if (currentColor == null)
return;
sb = new StringBuffer("function client_validation() {\n");
sb.append("if (document.forms[0]['");
sb.append(currentColor.getClientId(context));
sb.append("'].value=='red' ) {").append("\n");
sb.append("return true;");
sb.append("\n} else if (document.forms[0]['");
sb.append(currentColor.getClientId(context));
sb.append("'].value=='green' ) {").append("\n");
sb.append("return true;");
sb.append("\n} else if (document.forms[0]['");
sb.append(currentColor.getClientId(context));
sb.append("'].value=='blue' ) {").append("\n");
sb.append("return true;");
sb.append("\n } else {\n");
sb.append("alert('Enter red, green, or blue for the color.');");

sb.append("\nreturn false;\n");
sb.append("} \n }");
sb.append("\n");
if (writer != null) {
writer.startElement("script", script);
writer.writeAttribute("language", "JavaScript", "script");
writer.write(sb.toString());
writer.endElement("script");
}
}

Register the Custom Renderer

After creating the renderer, you need to register it with a render kit using the application configuration file. A render kit represents a set of renderers that can output markup suitable for one particular client.

Below is the render-kit element that registers the ColorRenderer, discussed in the previous section. The component-family element is a component type that identifies a component that this renderer can render. The renderer-type element is a renderer type that identifies the renderer. The renderer-class element is the actual name of the class.

<render-kit>
<renderer>
<component-family>javax.faces.component.UIOutput</component-family>
<renderer-type>Color</renderer-type>
<renderer-class>ColorRenderer</renderer-class>
</renderer>
</render-kit>

Before we were saying that your application could use multiple renderers, each of which can render the script appropriately for a particular client. If you have created a set of renderers, be sure to add corresponding renderer element tags in the application configuration resource file.

We'll see in the next section how the tag handler class uses the component type and the renderer type to tell the JavaServer Faces implementation how to render the component.

Creating the Tag Handler Class

One thing the tag handler does is to retrieve the type of the component associated with the tag. It also returns the type of the renderer (if there is one) to the JavaServer Faces implementation so that the component's encoding can be performed when the tag is processed.

Here are the methods from ScriptTag that perform these functions for our example:

public String getComponentType() {
return ("javax.faces.component.UIOutput");
}

public String getRendererType() {
return ("Color");
}

Notice that the component type and the renderer type elements returned by these methods must match the corresponding types configured by the render-kit element in the application configuration file. See the J2EE[TM] 1.4 Tutorial for more detail on how to create a tag handler class.

If your application uses multiple renderers, each of which can render the script appropriately for a particular client, you need to add something like the following to the getRendererType method of the tag handler class:

String agent = (String) getFacesContext().getExternalContext().getRequestHeaderMap().get("User-Agent");
if ((agent != null) && (agent.contains("Mozilla")) {
.... this is a Mozilla client, so return the type of the
renderer that handles this client ...

}...

This method determines what client is running the application and returns the appropriate renderer type. As described in the preceding section, you also need to register the corresponding renderers in the application configuration resource file.

The final step is to create the TLD file that defines your custom tag. Here is a piece of the TLD file from our example:

<taglib>
...
<display-name>customTags</display-name>
<tag>
<name>script</name>
<tag-class>ScriptTag</tag-class>
<body-content>empty</body-content>
...
</tag>
</taglib>

Finally, you include the taglib directive for the TLD file and the custom tag in the page, as shown here:

<%@ taglib prefix="customTags" uri="WEB-INF/customTags.tld" %>
...
<h:form id="validatorForm" onsubmit="return client_validation();">
<p>Enter a color (red, green, or blue):</p>
<h:inputText id="color" >
...
<customTags:script/>
</h:form>

Regardless of whether or not client side validation is provided, it is  good practice to validate data on the server as described previously in this document. The next section demonstrates how we added server-side validation to this example.

Including Server-Side Validation

If one of the standard validators that come with JavaServer Faces technology satisfies your needs, then use it. In that case, all you need to provide is the corresponding client-side validation script by creating a custom renderer.

If you need to provide your own server-side validation code, you can refer to the Server-side Validation entry in the Java[TM] BluePrints Solutions Catalog, which documents many techniques for performing custom server-side validation. You can employ any of these techniques when using server-side validation alongside client-side validation code in your application.

For our example, we have a custom validator class. The page author refers to this class using the standard validator tag. This solution makes the most sense in our case because the page author does not need to configure any validation parameters, and therefore we don't need to provide a custom tag to represent the validator on the page.

Here is the validate method from ColorValidator:

public void validate(FacesContext context, UIComponent component, 
Object toValidate) {
String value = null;
if ((context == null) || (component == null)) {
throw new NullPointerException();
}

if (!(component instanceof UIInput)) {
return;
}

if ( null == toValidate) {
return;
}

value = toValidate.toString();
if (value.equals("red") || value.equals("green") ||
value.equals("blue")) {
return;
} else {
System.out.println("value "+value);
FacesMessage errMsg = MessageFactory.getMessage(context,
COLOR_INVALID_MESSAGE_ID);
throw new ValidatorException(errMsg);
}
}
}

Notice that the custom validator has basically the same validation logic as the renderer class does. Unfortunately, this means that you must maintain two redundant pieces of code. Because you must manually write the script into a String in the renderer encoding methods and produce the Java code in the validator, it would be difficult to create the client-side and the server-side validation in place for easier maintenance.

If it helps in your particular case, you can access your custom validator from the encodeEnd method of the renderer through the component passed to the method:

public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
...
UIInput inputComponent = (UIInput) component;
Validator[] = inputComponent.getValidators();
// then get the validator you're looking for from the array
...
}

You might be able to generate the client-side script using the validation rules from the server-side validation code after accessing it from the encodeEnd method of the renderer. If the renderer can recognize the validator and can determine from the validator what checks it performs, it can subsequently generate the appropriate Javascript. For the renderer to access the server-side validation rules for this purpose, these rules must be defined by a custom validator class that the renderer recognizes rather than by a backing bean method, because it is impossible for a renderer to determine what validation rules the backing bean method is performing.

Another good solution is to have an external file that defines the set of validation rules that determine the behavior of both the client-side and server-side validation code. Struts Validator provides a client-side validation solution that allows you to do this. By using the struts-faces integration library, you can utilize Struts Validator in your JavaServer Faces application.

Using the Struts Validator With your JavaServer Faces Application

As we have demonstrated in this document, the renderers for the standard JavaServer Faces components do not provide support for client-side validation. To incorporate client-side validation into your application, you need to either produce a custom solution, as described in the preceding sections, or use a library offering support for client-side validation that you can use with your JavaServer Faces application.

The Struts-Faces integration library is used to integrate JavaServer Faces features into existing Struts applications.  You can also use it to integrate Struts features, such as the Struts Validator feature,  into your JavaServer Faces applications. Struts Validator provides a set of basic validators that come with JavaScript code that is already tested on multiple clients. The table below lists these validators.

  1. Struts Standard Validators
Validator Purpose
required Checks if the user entered a value
mask Checks if the value matches the regular expression given by the mask attribute
range Checks if the value is within two values
maxLength
Checks if the field's length is less than or equal to the value given by the max attribute
minLength Checks if the field's length is greater than or equal to the value given by the min attribute
byte, short, integer, long, float, double Checks if the value can be converted to one of these primitive types
date Checks if the value is a valid date and conforms to an optional pattern.
creditCard Checks if the value is a valid credit card number
email Checks if the value could be a valid email address

If you've already read the Server-side Validators blueprints topic, you'll notice that Struts has a larger set of standard validators than does JavaServer Faces technology. However, because the JavaServer Faces validation model is so flexible and extensible, JavaServer Faces implementors and validator and component vendors will most likely enrich this standard set of validators in the near future.

If your JavaServer Faces application already uses a standard JavaServer Faces validator and you want to add client-side validation to go along with it, you can create a custom renderer, as explained in Generating JavaScript Using a Custom Renderer. However, even though the standard server-side validation code is provided by the reference implementation, you still have the problem of two redundant pieces of code when you add the JavaScript to your application.

You can remedy this problem while making the set of Struts validators available to your JavaServer Faces application by using Struts Validator. This allows you to take advantage of a potentially more powerful client-side validation framework and still reap all the considerable benefits of using JavaServer Faces technology in the same application.

Adding Struts Validator to a JavaServer Faces Application

Incorporating Struts Validator in your JavaServer Faces application involves several steps:

  1. Download Struts and the Struts-Faces integration library.
  2. Add these JAR files to the WEB-INF/lib directory of your JavaServer Faces application: struts.jar, commons-validator.jar, commons-lang.jar, and jakarta-oro.jar, struts-faces.jar
  3. Declare the Struts servlet in the deployment descriptor (web.xml):
    <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>
    org.apache.struts.action.ActionServlet
    </servlet-class>
    <init-param>
    <param-name>config</param-name>
    <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
    </servlet>
  4. Add a mapping to the Struts servlet, as shown here.  Make sure that you use the same type of mapping (extension or prefix) that you use to map to FacesServlet.
  5. 	<servlet-mapping>
      <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
    </servlet-mapping>
  6. Declare all of the Struts TLDs that you will be using in your application.  You don't need to delcare the struts-faces TLD.
  7. Add the validator-rules.xml file that comes with the Struts project to the WEB-INF directory of your application. This file declares the server-side validator routines and defines the corresponding client-side JavaScript code used by the application.
  8. Import the struts-faces integration library into your page:

    <%@ taglib prefix="s" uri="http://struts.apache.org/struts/tags-faces" %>

  9. Add the javascript tag of the struts-faces integration library to the page, somewhere outside the form tag:

    <s:javascript formName="myForm" />

  10. Replace the h:form tag in your page with the s:form tag from the struts-faces integration library. Make sure the form tag has an ID and that it matches the formName attribute of the javascript tag.
  11. Add to the form tag an onsubmit attribute that invokes the JavaScript validate MyForm (this) function where MyForm matches the formName attribute and the ID of the form:
  12. <s:form id="myForm" onsubmit="return validateMyForm(this);">

  13. Create an XML file called validator.xml that defines how the validator routines included in validator-rules.xml are applied in your application. For example, if you have a page with a login form, and you want to restrict the length of the input to the username field (similar to what the standard JavaServer Faces ValidateLength validator can do), you can apply the Struts minlength and maxlength validators. The following example demonstrates restricting the input to at least 3 characters and no more than 16 characters. Each of the arg0, arg1, and arg2 tags refer to a message that is displayed if a particular kind of validation fails.

    <formset>
    <form name="logonForm">
    <field property="username"
    depends="minlength,maxlength">
    <arg0 key="prompt.username"/>
    <arg1 key="${var:minlength}" name="minlength"
    resource="false"/>
    <arg2 key="${var:maxlength}" name="maxlength"
    resource="false"/>
    <var>
    <var-name>maxlength</var-name>
    <var-value>16</var-value>
    </var>
    <var>
    <var-name>minlength</var-name>
    <var-value>3</var-value>
    </var>
    </field>
    ...
    </formset>
  14. Create an ActionForm bean that corresponds to the form that requires validation. This class contains properties that correspond to the UI components of the form.   Please refer to the Struts documentation for more information.
  15. Create an Action class for the purpose of processing a request.  Again, refer to the Struts documentation for more information.
  16. Include a struts-config.xml file, which specifies a resource bundle for validation messages, the validation configuration files, action mappings and any form beans for forms that contain fields that need to be validated.  Here is an example of each:
  	<form-beans>
<form-bean name="validatorForm"
type="com.sun.j2ee.blueprints.catalog.validator.LoginForm">
</form-bean>
</form-beans>
<action-mappings>
<action path="/logon"
type="com.sun.j2ee.blueprints.catalog.validator.LoginAction"
name="validatorForm"
scope="request"
input="validator"/>
</action-mappings>
<message-resources
parameter="com.sun.j2ee.bleuprints.catalog.validator.ApplicationMessages"/>

As you can see, using one of the standard struts validators requires a lot more work than using one of the standard server-side JavaServer Faces validators. For this reason, if you are not performing client-side validation, and one of the JavaServer Faces standard validators meets your needs, then you should use it.

On the other hand, because Struts Validator gives you a larger set of standard validators, each of which come with client-side validation code built-in, you should consider using Struts Validator in these cases:

If you are performing client-side validation for non-HTML clients, you will need to use a JavaServer Faces validator because you need a custom renderer to render the script appropriately for non-HTML clients.

Implementing a Custom Validator Using the Struts Validator

Struts Validator can also be used for implementing custom validators in a JavaServer Faces application. The example validation code from Adding Client-Side Scripting to a JavaServer Faces Page cannot be performed by any of the standard Struts validators. Regardless of whether you are using the JavaServer Faces validation model or Struts Validator, you still must implement a custom validator for this example. If this application runs only on HTML clients, you can use Struts, which might be a better solution because it requires less work to implement the custom validator and its centralized rules file makes it easier to maintain the validation code.

To create and use a custom validator, you need to do the following:

  1. Define the validation rules in your validation.xml file.
  2. Implement a method that handles the server-side validation.
  3. Update the validator-rules.xml file with a validator element representing your new validator. This new element will also define the client-side JavaScript code.

Creating this custom validator is outside the scope of this entry; instead, we leave it as an exercise for the reader.  The important thing to note is that the rules for performing the validation are defined in the centralized validation file, which is unique to the application. Changing the rules in this file will change both the server-side and client-side validation behavior because the code for each type of validation accesses the rules in this file.

The code that performs the validation for our example can be more generalized so that it can compare any set of String values, unlike ColorValidator or ColorRenderer, which only check if input matches the strings "red", "green", or "blue".  You define which strings to compare in validation.xml.  Of course, this means you have an easier time synchronizing the client-side and server-side code.

After creating your own validator, you need to declare it and define its corresponding client-side JavaScript in the same validator-rules.xml file that includes the declarations and definitions of the standard Struts validators.

For help with creating a custom validator with Struts, read the section Pluggable Validators in the Struts Validator Guide.

Using Struts Validator for creating custom validators has many benefits. By defining the validation rules separately from the behavior of the validation code, you can make the validation code easier to maintain, more pluggable, and less tightly coupled to the application code.

References

For more information about this topic, refer to the following:


© Sun Microsystems 2004. All of the material in The Java BluePrints Solutions Catalog is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.