Ajax Form Validation Using Spring and DWRValidating input is a critical element to almost any non-trivial software application. Regardless of the purpose of the application, its architecture, or the platform on which it will run, it is standard practice to verify that all input is valid before being accepted. In three-tier web applications, the choice must be made to validate user input on either the server, the client, or both. Server-side validation is the industry standard but it requires the user to fill out the form, click Submit, and wait for a page refresh. While the best technical solution, it provides a poor but accepted user experience. Although client-side validation, using JavaScript, provides immediate feedback and an enhanced user experience, it has many disadvantages such as being tedious and error-prone to develop, being hard to test, and leading to fragile code. Inconsistent implementations across different browsers and the fact that a user can disable JavaScript is enough to conclude that client-side validation alone is not sufficient. Although a hybrid approach of performing validation on both the client and server may seem like a good solution on the surface it doesn't take long to realize that the increased development time, duplication of logic, and inevitable maintenance and testing problems quickly outweigh the advantages.
An ideal solution would be to write your validation logic on the server and then have some way of invoking it directly from the client, giving the appearance of client-side validation. Should some circumstance cause this seemingly client-side validation to not be executed correctly, such as a browser incompatibility or JavaScript being turned off, this mechanism should fail gracefully and allow the traditional server-side validation processing to work as usual. In effect, you would gain all the advantages of the hybrid approach while avoiding its disadvantages and the disadvantages of purely client-side validation.
With the rise and subsequent acceptance of Ajax, you can now do exactly that. Using what is referred to as Ajax form validation, just after a user finishes filling out an input form field a background process of the browser can transparently transmit the form input value to the server. The server then performs validation of the input values. Should the server determine an input value is invalid, a validation error message is returned to the browser and dynamically displayed next to the invalid form input entry. While some advanced web applications have demonstrated this functionality for a short while, the software has typically been hardwired and custom-coded for each form or for each application.
This article presents a generic and reusable Ajax form validation design that, by using existing open source components and leveraging your applications' existing validation logic, can easily and quickly be used to Ajax validate every form of your web-based applications. To provide a concrete example, the registration form of the jPetstore sample application, provided by the Spring framework, has been modified. This article outlines the steps taken to modify jPetstore and discusses the design- and implementation-level details.
Before getting started, it should be noted that this article makes use of the Spring framework and a small amount of Spring and Spring MVC familiarity is assumed. Readers needing additional Spring documentation should see the Resources section. If you are not familiar with Spring, don't panic. Conceptually, all of the information presented applies equally to any web-based MVC framework and it would be trivial to modify the examples provided to work with Struts or JSF, for example.
The following steps will guide you through getting the sample code up and running:
With the sample application up and running, we can move along and discuss the three components required to perform Ajax validation: the server-side validation infrastructure and logic, an Ajax communication framework to marshal information between the server and client, and the client-side JavaScript used to control the process. Because the use of a server-side validation mechanism is probably the most familiar and because your current or planned application would undoubtedly have some server-side validation, we'll start there.
The first component needed to incorporate Ajax validation into
your web application is a server-side validation framework. Spring,
a full-stack Java/J2EE application framework that has gained
tremendous popularity, and its validation framework are
demonstrated through the jPetstore sample application. Although
Spring offers incredibly powerful functionality across the entire
spectrum of Java software development, two of Spring's greatest
strengths are its simplicity and straightforward design. Input
validation is an area that is particularly straightforward. The
following jPetstore validation code provides an example of a
typical Spring Validator:
public class AccountValidator implements Validator {
...
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "firstName", \\
"FIRST_NAME_REQUIRED", "First name is required.");
ValidationUtils.rejectIfEmpty(errors, "lastName", \\
"LAST_NAME_REQUIRED", "Last name is required.");
ValidationUtils.rejectIfEmpty(errors, "email", \\
"EMAIL_REQUIRED", "Email address is required.");
ValidationUtils.rejectIfEmpty(errors, "phone", \\
"PHONE_REQUIRED", "Phone number is required.");
ValidationUtils.rejectIfEmpty(errors, "address1", \\
"ADDRESS_REQUIRED", "Address (1) is required.");
ValidationUtils.rejectIfEmpty(errors, "city", \\
"CITY_REQUIRED", "City is required.");
ValidationUtils.rejectIfEmpty(errors, "state", \\
"STATE_REQUIRED", "State is required.");
ValidationUtils.rejectIfEmpty(errors, "zip", \\
"ZIP_REQUIRED", "ZIP is required.");
ValidationUtils.rejectIfEmpty(errors, "country", \\
"COUNTRY_REQUIRED", "Country is required.");
}
...
}
While providing a functional example for a sample application, a major drawback to this approach is that the logic is designed to operate on the entire HTML form. An unfortunate consequence is that validation of an individual form field, required to support Ajax validation, is not possible. With a small amount of refactoring, this drawback can be avoided. The resulting code is:
...
public void validate(Object obj, Errors errors) {
Account account = (Account) obj;
validateFirstName(account.getFirstName(), errors);
validateLastName(account.getLastName(), errors);
validateEmail(account.getEmail(), errors);
validatePhone(account.getPhone(), errors);
validateAddress1(account.getAddress1(), errors);
validateCity(account.getCity(), errors);
validateState(account.getState(), errors);
validateZip(account.getZip(), errors);
validateCountry(account.getCountry(), errors);
}
...
public void validateFirstName(String firstName, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "firstName", \\
"FIRST_NAME_REQUIRED", "First name is required.");
}
public void validateLastName(String lastName, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "lastName", \\
"LAST_NAME_REQUIRED", "Last name is required.");
}
...
By casting the parameter obj into an instance of
the form backing object and extracting the logic of the original
validate() method into separate methods intended to
validate a single form field, this seemingly cosmetic refactoring
provides a handful of benefits. First, the new code results in a
number of short, highly cohesive, easy-to-read methods. The logic
to validate the first-name input field is easily and quickly found
within the validateFirstName() method instead of being
scattered throughout the original long "validate
everything" method. Second, this level of organization and
structure will pay off should your HTML forms or their validation
logic grow in complexity and/or length. Third, and most
importantly, because there are methods to validate individual input
fields, Ajax validation can now be supported.
When a form submission is received, Spring's form handling
process works by first instantiating an instance of the form
backing object. Next, a form input element is bound to the backing
object by using reflection to call the appropriate setter method
with the form input value passed as an argument. This binding
process is repeated for each input element. Finally, with the
backing object passed as a parameter, the validate() method on a
validator is called. Because an Ajax validation request is not a
normal form submission and contains only a form input ID and value,
Spring's normal process can't be used and the instantiation and
binding must be done "manually." However, instead of
invoking the Validator.validate() method, which expects an instance
of the form backing object, we will invoke the validation method
for an individual field. The following code, located in the
AccountValidator class and exposed to the
client, performs these manual instantiation, binding, and
validation invocation steps:
/**
* Get the validation message for an individual
* input field of a model object.
*
* @param modelObject The object to validate against.
* @param formInputId The id attribute of the form input field.
* @param formInputValue The input value to be validated.
* @return The validation message.
*/
public String getInputFieldValidationMessage(String formInputId, String formInputValue) {
String validationMessage = "";
try
{
Object formBackingObject = new Account();
Errors errors = new BindException(formBackingObject, "command");
formInputId = formInputId.split("\\.")[1]; // Ignore the preceding "command." portion of the id
String capitalizedFormInputId = StringUtils.capitalize(formInputId);
// Invoke the set[formInputId] method on the Account instance
String accountMethodName = "set" + capitalizedFormInputId;
Class setterArgs[] = new Class[] { String.class };
Method accountMethod = formBackingObject.getClass().getMethod(accountMethodName, setterArgs);
accountMethod.invoke(formBackingObject, new Object[] { formInputValue });
// Invoke the validate[formInputId] method of the AccountValidator instance
String validationMethodName = "validate" + capitalizedFormInputId;
Class validationArgs[] = new Class[] { String.class, Errors.class };
Method validationMethod = getClass().getMethod(validationMethodName, validationArgs);
validationMethod.invoke(this, new Object[] { formInputValue, errors });
validationMessage = getValidationMessage(errors, formInputId);
}
catch (Exception e)
{
// Handle appropriately for your application
System.out.println("New code exception: " + e);
}
return validationMessage;
}
If getInputFieldValidationMessage were passed a
formInputId of username, the preceding code would call
setUsername() to bind the formInputValue parameter to
a newly instantiated instance of Account. Next,
validateUsername would be invoked on the
AccountValidator class. Finally, the following
getValidationMessage() would be used to take advantage
of Spring's underlying form validation messaging mechanism and
retrieve the actual text that is returned to the browser and
presented to the user.
/**
* Get the FieldError validation message from the underlying MessageSource for the given fieldName.
*
* @param errors The validation errors.
* @param fieldName The fieldName to retrieve the error message from.
* @return The validation message or an empty String.
*/
protected String getValidationMessage(Errors errors, String fieldName)
{
String message = "";
FieldError fieldError = errors.getFieldError(fieldName);
if (fieldError != null)
{
message = messageSource.getMessage(fieldError.getCode(), null,
"This field is invalid", Locale.ENGLISH);
}
return message;
}
Ajax has quickly matured to a state where many open source Ajax frameworks exist. You'll be doing yourself a favor by using one of these frameworks instead of creating your own implementation. Direct Web Remoting (DWR), a java.net project created by Get Ahead, is an outstanding choice. Being easy to use while providing advanced features, DWR is well documented and straightforward to integrate into your application. Well designed, DWR works by dynamically generating JavaScript based on Java classes. Using a servlet and the necessary JavaScript infrastructure, JavaScript calls made in the browser are transparently sent to the server, invoked on the Java class, with the results sent back to the browser and available in JavaScript. Because DWR is well documented, only a brief description of the steps taken to integrate it into the sample application are presented. For a more complete explanation of DWR, I refer you to the design overview and getting started sections of the project's website.
The following steps were used to integrate DWR into the sample application:
Download dwr.jar from the project's download page. This article uses version 1.1.4.
Add the downloaded dwr.jar to the sample web application lib directory.
Include the following servlet definition and mapping in the web.xml:
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
...
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
Because the sample application uses the Spring framework for
validation logic, we need some way to tell DWR to instantiate
the Spring validation bean classes and expose them through
JavaScript. Fortunately, DWR provides Spring integration out of the
box through SpringCreators. The DWR configuration
file, named dwr.xml and located in the same
directory as web.xml, is as follows:
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
"http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
<allow>
<create creator="spring" javascript="AccountValidatorJS">
<param name="beanName" value="accountValidator"/>
<include method="getFieldInputValidationMessage"/>
</create>
</allow>
</dwr>
The file instructs DWR to create a JavaScript object named
AccountValidatorJS. With DWR handling all of the
details, when the client calls a JavaScript function on the
AccountValidatorJS object, a server-side Java method
of the same name is invoked on Spring's instance of the
AccountValidator class. This is how server-side logic,
in this case validation logic, is exposed to the client through
Ajax.
With the server-side validation logic and Ajax communication framework in place, the third and final component is to modify the web tier to support the Ajax validation process. Each page containing a form that is to be Ajax validated needs to have a small amount of JavaScript infrastructure added. An example of this infrastructure is listed below:
...
<script type='text/javascript' src='/jpetstore/dwr/engine.js'></script>
<script type='text/javascript' src='/jpetstore/dwr/util.js'></script>
<script type='text/javascript' src='/jpetstore/dwr/interface/AccountValidatorJS.js'></script>
<script language="JavaScript">
// Swallow errors and warnings from DWR; handle appropriately in your app
DWREngine.setErrorHandler(null);
DWREngine.setWarningHandler(null);
function validateAccountInputField(element)
{
var id = element.id;
var value = element.value;
AccountValidatorJS.getInputFieldValidationMessage(id, value, {
callback:function(dataFromServer) {
setInputFieldStatus(element.id, dataFromServer);
}
});
}
function setInputFieldStatus(elementId, message)
{
var id = "" + elementId + "Error";
document.getElementById(id).innerHTML = message;
}
</script>
...
After including two core DWR scripts on the first two lines, the
third line includes the generated AccountValidatorJS
proxy object. While the first two scripts will typically be
included on any page in which you use DWR, the third line is
specific to which server-side Java object you intend to take
advantage of and will vary from page to page.
Next, the the global error and warning handlers are set to null, thereby swallowing all DWR errors and warnings. While acceptable for the purposes of this article, this is obviously not sufficient for enterprise-level software and I strongly suggest a different design for for your production applications.
The validateAccountInputField() function is the
workhorse of the client-side validation. By accepting a reference
to the form input element that is to be validated, the
AccountValidatorJS proxy object is used to transmit
the input field's id and value to the
server for validation. Although the syntax is cryptic and hard to
read, the third parameter to the
AccountValidatorJS.getInputFieldValidationMessage()
function instructs DWR to invoke the
setInputFieldStatus() callback function when the
response from the server is available. At that time, the
setInputFieldStatus() function performs the dynamic
display and update of the on-screen validation message. By
appending Error to the element's id, a
reference to the span used for visual display is
obtained and its innerHTML is updated with the content
of the response validation message.
Finally, validateAccountInputField() needs some way
of being invoked when the user alters an input field. This is
accomplished by adding an onChange() event listener to
each form input you wish to Ajax validate. A snippet from an
example form is shown below:
...
<form action="<c:url value="/shop/newAccount.do"/>" method="post">
...
<TR bgcolor="white">
<TD><font color="red">* </font>First name:</TD>
<TD>
<spring:bind path="accountForm.account.firstName">
<input type="text"
id="<c:out value="${status.expression}"/>"
value="<c:out value="${status.value}"/>"
onChange="validateAccountInputField(this);"
/>
<span id="<c:out value="${status.expression}"/>Error" class="error">
<c:out value="${status.errorMessage}"/>
</span>
</spring:bind>
</TD>
</TR>
...
</form>
...
While the use of Spring's form-handling capabilities will be
unusual to those not familiar with Spring MVC, the rest is a
typical HTML form with a span located next to the
input field used to display the validation message. The use of an
onChange() event listener will invoke
valdiateAccountInputField() when a user changes the
value of this input field. Using this to pass a
reference of itself to the validateAccountInputFiled()
function, the code is generic for every input field and can quickly
be added to to all of your forms.
Figure 1 demonstrates the web tier UI after a user has modified the form. In this particular case, the user filled out the form and subsequently removed all input except for the user ID field. As the user removed the input and the browser focus shifted to a different input, the Ajax validation was invoked and a message indicating that that field was required was dynamically displayed. Of particular interest is the user ID input field, with the dynamic message that that username is not available.

Figure 1. Display of dynamic validation messages
Using the jPetStore sample application, this article discussed
and demonstrated a design for a generic and reusable Ajax form
validation solution. Using existing open source projects and
leveraging your applications validation logic, the development time
and complexity required to implement this solution into your
applications is minimal. After setting up the necessary
infrastructure (and possibly some minor refactoring to existing
applications), the largest effort will probably be simply adding
onChange() event listeners to the HTML form input
elements you'd like to validate via Ajax. While this article made use
of the Spring and DWR frameworks, nothing about Ajax or the Ajax
validation concepts presented here are tied to any particular tool,
framework, or language, and implementations can be created in any
way that fits your particular technical or business
requirements.
Eric Spiegelberg is a Minneapolis based Java/EE consultant.
|
|