Spring MVC: Validation

Options

Two current options:

  • create a class that implements Spring’s Validator interface and make validation rules programmatically
  • use commons-validator declarative validation, integrated into Spring in the springmodules projects (ie, not part of the core distribution)

A forthcoming (only-available-through-CVS-so-far) third option:

  • Valang, a validation language so that validations can be written declaratively in a ValangValidatorFactoryBean declaration in an xml file, and this will create an implementation of Spring’s Validator. This is in the springmodules sandbox but has not yet been released.

(Update 2005-09-12: Valang is still only available through CVS, but some recent traffic on springframework-developer includes praise from several people using it in production who are very happy with it.
Update 2005-09-15: An addition to Valang adds Valang to JavaScript translation support, so Valang rules can now also automatically generate client-side JS validation.)

Example

For example, suppose a form with firstName, lastName, and userid fields which is invalid if all the fields are blank or if there is both a firstName/lastName and a userid.

The xml snippets could go in the applicationContext.xml file, but since it is web-layer-specific, it makes more sense to put it in an action-servlet.xml file. We will also assume that we’ve got a User class with those fields, and we’ve identified it as the command class either in the form controller’s bean definition or in its constructor.

In all three cases, add a validator property to the formController bean definition:

<bean id="findUserForm" ...
    <property name="validator" ref="findUserValidator" />

Programmatically

Create a FindUserValidator class that implements Validator

interface Validator {
    boolean supports(Class clazz);
    void validate(Object obj, Errors errors);
}

checks the rules we’re interested in

class FindUserValidator implements Validator {

    public boolean supports(Class clazz) {
        return clazz.equals(User.class);
    }

    public void validate(Object obj, Errors errors) {
        User user = (User) obj;
        if (isEmpty(user.getFirstName()
                && isEmpty(user.getLastName()
                && isEmpty(user.getUserid()) {
            errors.addError("", "error.blank", ...
        } else if (!isEmpty(user.getUserid()
                && (!isEmpty(user.getFirstName() ||
                !isEmpty(user.getLastName())) {
            errors.addError("", "error.collision", ...
        }
}

and add it to the context:

<bean id="findUserValidator"
        class="{package}.FindUserValidator" />

Commons-validator

Add commons-validator and springmodules to the project.

Add some boilerplate to the context so it knows about commons-validator:

<bean id="validatorFactory"
        class="org.springmodules.commons.validator.DefaultValidatorFactory">
    <property name="validationConfigLocations">
        <list>
            <value>/WEB-INF/validator-rules.xml</value>
            <value>/WEB-INF/validation.xml</value>
        </list>
    </property>
</bean>

<bean id="beanValidator"
        class="org.springmodules.commons.validator.DefaultBeanValidator">
    <property name="validatorFactory" ref="validatorFactory"/>
</bean>

Change the validator property on the form controller to point at the beanValidator (validation.xml can contain validation for the entire app, not just this form controller)

<bean id="findUserForm" ...
    <property name="validator" ref="beanValidator" />

Add the validation rules to validation.xml. (In fact, having validation rules that check more than one field is listed as a nice-to-have on the ValidatorWishList, so it may not be possible to implement the rules for this specific example with commons-validator after all.)

Valang

Add springmodules (from CVS) to the project and add a validator bean definition to the context:

<bean id="findUserValidator"
        class="org.springmodules.validation.ValangValidatorFactoryBean">
   <property name="valang">
      <value><![CDATA[
{ empty : firstName is blank and lastName is blank and userid is blank
        : 'At least one field must be filled in' }
{ collision : (firstName is not blank or lastName is not blank)
        and userid is not blank :
        'Name and userid must not both be filled in' }
      ]]></value>
   </property>
</bean>

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply