Ajax Form Validation Using Spring and DWR, Revised Blog


    In "Ajax Form Validation Using Spring and DWR," I presented a design to use Ajax to perform client-side validation by invoking an application's server-side validation logic. The whole point was to easily leverage validation logic on the server to prevent duplication of code, to reduce the time and cost of development and maintenance, and to enhance the usability of a web application. Few non-trivial software designs get it right the first time. This design has been running in production systems for over a year (its publication is approaching the ten-month mark), and has shown some limitations. This article discusses the emerged limitations and then presents an improved design that not only addresses the limitations but provides added functionality. Conceptually, the two designs are very similar, and a familiarity with the previous article, or working knowledge of Ajax, DWR, and the Spring Web MVC framework, are assumed.

    Before diving into the details, let's review the general flow of an Ajax validation request. When the user changes an input field, JavaScript is used to collect and bundle the necessary form input and transmit it to the server. On the server, some validation process is invoked. The results are returned to the browser, where they are interpreted and used to update the browser's DOM to visually display or hide validation messages. The browser's Ajax call is essentially asynchronously submitting a custom form, a JavaScript callback function is used to handle a custom response, and the custom response is used to dynamically update the page.

    Reasons for Change

    The previous design had limitations. To support being invoked through reflection, a design format was imposed on the SpringValidators. While creating a separate validation method for each input field is a good approach, on complex forms it lead to a high number of short and therefore seemingly cluttered methods. Many readers provided feedback that they, for one reason or another, could not or would not refactor their existingValidators.

    Second, the original design contained a fair amount of redundant boilerplate and hardcoded configuration. Every Ajax validated form had to have its Validator's DWR proxy configured in the dwr-config.xml file, which became unwieldy in large applications. The JavaScript used to invoke validation had DWR proxy names embedded in the script, which required the JavaScript to be customized each time it was put in use on a new form.

    Third, only single input field validation was supported. While this was acceptable for the vast majority of requirements, the fact remained that there are situations where validation of input is more complex and validation of a "dependent" input field requires the "parent" input field(s) to provide context to the validation process. Examples of this situation include validating that a user's zip code exists within their state (does 55431 exist in MN?), that a credit card number belongs to the specified card type (does the card number conform to the format of a Visa card?), and that a password confirmation input field matches what the user entered in the password input field. Since only a single input field was submitted for validation at a time, this type of complex validation was not possible.

    Fourth, the previous design contained custom code that duplicated functionality available in the Spring and DWR projects.

    With these weaknesses in mind, the goals of a redesign were to simplify the architecture, minimize configuration, replace custom code with existing Spring and DWR features, and support validation of multiple input fields.

    Server-Side Validation Using Spring

    The pain of having to configure a DWR proxy for everyValidator led to the realization that the use of a single front controller would be a great improvement over a front controller per form. This would eliminate the majority of the configuration in both the Java and JavaScript layers and would lead to a much more reusable solution.

    Because every Ajax validation request now runs through a single front controller, this front controller must be smart enough to be able to determine which Spring Web MVC Controllerwould have been used to handle a traditional submission from the same form. Once this Controller is known, Spring's flexible and powerful API provides access to all the functionality needed to dynamically perform the validation process.

    Before we go into the details of how this is accomplished, let's quickly review how Spring Web MVC works. When configuring Spring Web MVC for use within your application, theDispatcherServlet (or similar) is configured to know which Controller is used to process a request for a given URI. A typical dispatcherServlet.xml configuration file contains something similar to:

    ... <bean name="/shop/newAccount.do" class="AccountFormController"> <property name="petStore" ref="petStore"/> <property name="validator" ref="accountValidator"/> <property name="successView" value="index"/> </bean> ...

    These mappings tell Spring Web MVC that when it receives a request URI of /shop/newAccount.do to delegate execution to the AccountFormController.

    The core concept of the revised middle tier is that if the Ajax validation request included the value of the form's action attribute, the validation front controller could use theDispatcherServlet's configuration mappings to programatically determine which Controller would be used to handle a normal form submission. Just as when theDispatcherServlet receives a traditional form submission to an action URI of /shop/newAccount.do and delegates to the AccountFormController, the validation front controller can determine that a validation request that includes /shop/newAccount.do should be handled byAccountFormController as well. The following code fromSpringAjaxController does just that:

    ... protected BaseCommandController getController(String actionUri) { BaseCommandController baseCommandController = null; try { // Attempt to find the controller by bean name baseCommandController = (BaseCommandController) applicationContext.getBean(actionUri); LOG.debug("Found baseCommandController (by bean name): " + baseCommandController); } catch (NoSuchBeanDefinitionException nsbde) { LOG.debug("BaseCommandController not found by bean name, " + searching AbstractUrlHandlerMappings"); } if (baseCommandController == null) { Map<String, AbstractUrlHandlerMapping> map = applicationContext.getBeansOfType(AbstractUrlHandlerMapping.class); for (String mappingName : map.keySet()) { AbstractUrlHandlerMapping mapping = map.get(mappingName); Map<String, BaseCommandController> handleMap = mapping.getHandlerMap(); baseCommandController = handleMap.get(actionUri); if (baseCommandController != null) { LOG.debug("Found baseCommandController (by " + "AbstractUrlHandlerMapping): " + baseCommandController); break; } } } return baseCommandController; } ...

    Although updated to take advantage of advanced Spring functionality, from this point the new design follows the same general process as the old design: dynamically instantiate an instance of the command class, bind the request input to the new command class instance, invoke all Validators associated with the Controller, and return the results to the browser. This code is also found in theAccountFormController:

    ... LOG.debug("Instantiating command"); Class<Object> commandClass = controller.getCommandClass(); Object command = BeanUtils.instantiateClass(commandClass); LOG.debug("Populating command object with request values"); BeanWrapper beanWrapper = new BeanWrapperImpl(command); beanWrapper.setPropertyValues(nameValuePairsMap); LOG.debug("Invoking validators"); String commandName = controller.getCommandName(); Errors errors = new BindException(command, commandName); Validator validators[] = controller.getValidators(); for (Validator validator : validators) { ValidationUtils.invokeValidator(validator, command, errors); } // For each input, get the first error message and assemble Locale locale = LocaleContextHolder.getLocale(); LOG.debug("Building output"); for (String inputId : nameValuePairsMap.keySet()) { String inputValue = nameValuePairsMap.get(inputId); String unqualifiedInputId = StringUtils.unqualify(inputId); String args[] = { unqualifiedInputId, inputValue }; String message = validationMessageFormatter.getFieldErrorMessage(errors, inputId, args, locale); if (message == null) { // There was no validation message, return an empty String message = ""; } resultMap.put(inputId, message); } return resultMap;

    After instantiating an instance of the command class, the input values from the browser must be bound to this instance. Although the binding process is still manual, in the sense that Spring Web MVC does not do it for us, the use of Spring'sBeanWrapper and BeanWrapperImpl concrete implementation take care of all the actual work, including the binding of multiple inputs. Because the BeanWrappersupports nested paths, the input id and values transmitted from the browser can be used to populate complex domain objects that back the form. For example, if the validation request contained"user.firstname=Ted&user.lastname=Anderson&user.address.zipCode=55311", the BeanWrapper would follow JavaBean naming conventions and would set the firstname property of aUser object to the value of Ted, thelastname property to a value of Anderson, and 55311 would be set to the zipCodeproperty of the User's underlying Addressobject.

    With the service layer to process validation messages in place, we next need to discuss how communication with the server has been revised. In the next section, you will see how DWR is used to expose and invoke the validation front controller.

    Ajax Using DWR

    As with the previous design, Direct Web Remoting (DWR) is used for Ajax communication between the client and middle tiers. 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 and invoked on the Java class, with the results returned to the browser and available in JavaScript. This is how server-side logic--in this case, Spring Web MVC validation logic--is exposed to the client through Ajax.

    With the goal of simplifying configuration, the updated design takes advantage of the newly released DWR 2.0 and its optional annotation-based configuration. Written by Maik Schreiber, the annotations can be used as a replacement or in combination with thedwr.xml configuration file used in DWR 1.x. By using annotations, the problem of a large and potentially bloateddwr.xml configuration file can be avoided.

    As described in the DWR annotation documentation, configuring your application to use DWR annotations is a three-step process. First, you must specify the DWR controller servlet in your web.xml file. Second, the fully qualified class name of each class that is to be exposed through DWR needs to be added to a comma-separated list in theweb.xml file. Third, each one of these classes needs to be decorated with DWR annotations. To do this, begin by decorating each class with a @RemoteProxy annotation. By default, the class name is used as the JavaScript scripting name (the name of the JavaScript proxy object). It's good practice to expose as little information as possible about the Java layer in JavaScript, and the name attribute of the@RemoteProxy annotation is used to explicitly specify the scripting name of the JavaScript proxy object. Because the class we want to expose to JavaScript is a Spring-managed bean, DWR also needs to be instructed to use a SpringCreator and to locate the bean in Spring's ApplicationContext by a specific name. Finally, decorate each method to be made available through remote access with a @RemoteMethod annotation. Naturally, those methods without this annotation will not be remotely accessible. Let's see an example of the updatedSpringAjaxController:

    @RemoteProxy(name="AjaxFormValidatorJS", creator=SpringCreator.class, creatorParams = @Param(name = "beanName", value = "ajaxFormValidator")) public class SpringAjaxController implements ApplicationContextAware { ... @RemoteMethod public Map<String, String< validateString(String formActionUri, String inputIdValuePairs) { Map resultMap = new HashMap(); BaseCommandController controller = getControler(formActionUri); ... } ... } 

    You can see that we're instructing DWR to use aSpringCreator, that we want to locate the bean by a bean name, and the bean name we're looking for in the SpringApplicationContext is ajaxFormValidator. DWR will generate a JavaScript object namedAjaxFormValidatorJS to proxy the Java-based instance on the server. This object will have avalidateString() function that when invoked executes the method of the same name on the server.

    It's worth noting that decorating classes with DWR annotations creates an otherwise unnecessary compile-time dependency between the classes and DWR. This could become problematic if you want to reuse your Java layer on a project or in an environment where DWR cannot be used. While this will mostly likely be a small and academic issue for most developers, it's still a design issue to be aware of.

    Once the validateString() method has completed theMap of validation message(s) must be returned to the browser in a format usable in JavaScript. The previous design manually assembled the results into a String of name-value pairs and transmitted it to the browser, where it was manually parsed. Not only providing an excellent mechanism to invoke Java from JavaScript (and vice versa), DWR also assists in converting Java objects into a representation that can easily be used within JavaScript. Because the validateString()method returns a Map, DWR will use a built-inConverter to transparently convert the map into a JavaScript array. As you'll see in the next section, this array will be used to by a callback method to dynamically update the page with validation error messages.

    Controlling the Process with JavaScript

    Although it has been updated to include the ability to submit multiple input id and value pairs, to eliminate hardcoded DWR proxy references, and to include the value of the form's action attribute in the Ajax request, conceptually the JavaScript used to support Ajax validation functions the same as the original version. Each Ajax-validated form input has anonChange() event listener registered that will invoke the validate() function when the value of that input field has been changed.

    Let's look at the details:

    <script type='text/javascript'> function validate(inputArray) { var request = ""; var formAction = ""; if (inputArray.length == null) { <%-- Submit a single input field and value --%> request = formatInput(inputArray.id, inputArray.value); formAction = getFormAction(inputArray.id); } else { <%-- Submit multiple input field and value pairs --%> for (var i = 0; i < inputArray.length; i++) { var input = document.getElementById(inputArray[i].id); var nameValuePair = formatInput(input.id, input.value); request = request + nameValuePair; if (i != inputArray.length) { request = request + "&"; } } formAction = getFormAction(inputArray[0].id); } <%-- Invoke server-side logic --%> AjaxFormValidatorJS.validateString(formAction, request, handleValidationResponse); } function handleValidationResponse(response) { <%-- The response is an array of id/message value pairs --%> for (inputId in response) { var errorElementId = inputId + ".errors"; var validationMessage = response[inputId]; dwr.util.setValue(errorElementId, validationMessage); } } function getFormAction(inputId) { var currentElement = document.getElementById(inputId); while (currentElement != null) { if (currentElement.tagName.toLowerCase() == "form") { <%-- Drop the http://servername --%> var formAction = ""; var locationFragements = currentElement.action.split("/"); for (var i = 4; i < locationFragements.length; i++) { formAction = formAction + "/" + locationFragements[i]; } return formAction; } else { currentElement = currentElement.parentNode; } } } ...

    The validate() function is responsible for initiating the validation process and its input will be either a single form input element or an array of form input elements. Thelength property of the argument will indicate if the input is a single input element or an array of input elements. In the case where the argument is a single form input element, the element's id and value are assembled into a single name-value pair. In the case where the argument is an array of form input elements, the id and input values of each element in the array are assembled into single a name-value pair. Regardless of which case is executed, the end result is aString containing id-value pairs of each form element that will be transmitted to the server for validation. It is this ability to process either an individual element or an array of elements that provides client-side support for multi-input validation.

    As we've seen from previous sections, the server-side validation logic needs the value of the form's action attribute included in the request. Looking at the previous sample code, thegetFormAction() function starts from the input element that fired the onChange() event and upwardly navigates the browser's DOM until the form element is found. With a reference to the form element, the form'saction attribute is read and then included as part of the validation request transmitted to the server. By dynamically determining the value, the JavaScript is much more flexible and reusable. As such, this script will work with any form and can be globally included on all pages (tools such as Tiles or SiteMesh makes this particularly easy).

    Using the DWR-generated AjaxFormValidatorJS object, the request String (composed of the input name-value pairs and form action attribute value) is sent to the server for validation and the browser awaits a response.

    When the response is returned from the server, thehandleValidationResponse() callback function is invoked with the sever-generated array of DOM elementid and validation messages. Every input element sent to the server will have a corresponding entry in the response array. The handleValidationResponse() function iterates through the array and passes the element idand validation message to DWR's dwr.util.setvalue()function, which updates the particular element with the validation message. Browser DOM changes take effect immediately and it is this show/hide effect that gives the appearance of client-side validation and greatly enhances the user experience of the web application.


    This article discussed weaknesses in a previously presented design allowing easy integration of Ajax form validation into web-based applications. After discussing the weaknesses, an evolved but simplified design was presented that used a single front controller, DWR annotations and Converters, and updated JavaScript to dramatically reduce configuration and maximize reusability. By replacing custom code with Spring functionality, the code is not only simplified but now supports multiple input validation. Finally, by no longer imposing a design format on Spring Validators, existing application code can be Ajax-enabled without refactoring.