Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

annotation based Spring bean validation

I'm investigating an annotation-based approach to validating Spring beans using spring modules. In this tutorial, the following bean (getters and setters omitted) is used as an example:

public final class User {  

  @NotBlank  
  @Length(max = 80)  
  private String name;  

  @NotBlank  
  @Email  
  @Length(max = 80)  
  private String email;  

  @NotBlank  
  @Length(max = 4000)  
  private String text;  
}

The error message that is used if a particular validation rule is disobeyed should follow this format:

bean-class.bean-propery[validation-rule]=Validation Error message

Examples for the class shown above include:

User.email[not.blank]=Please enter your e-mail address.  
User.email[email]=Please enter a valid e-mail address.  
User.email[length]=Please enter no more than {2} characters.

The fact that the message keys contain the class name presents a couple of problems:

  1. If the class is renamed, the message keys also need to be changed
  2. If I have another class (e.g. Person) with an email property that is validated identically to User.email, I need to duplicate the messages, e.g.

    Person.email[not.blank]=Please enter your e-mail address.
    Person.email[email]=Please enter a valid e-mail address.
    Person.email[length]=Please enter no more than {2} characters.

In fact, the documentation claims that is possible to configure a default message for a particular rule (e.g. @Email) like this:

email=email address is invalid

This default message should be used if a bean-specific message for the rule cannot be found. However, my experience is that this simply does not work.

An alternative mechanism for avoiding duplicate messages is to pass the key of the error message to the rule annotation. For example, assume I have defined the following default error message for the @Email rule

badEmail=Email address is invalid

This message should be used if I annotate the relevant property like this:

@Email(errorCode="badEmail")
private String email;

However I tried this, out and again, it just doesn't seem to work. Has anyone found a way to avoid duplicating error messages when using this validation framework?

like image 303
Dónal Avatar asked Sep 29 '08 16:09

Dónal


People also ask

What is @valid annotation in Spring?

The @Valid annotation will tell spring to go and validate the data passed into the controller by checking to see that the integer numberBetweenOneAndTen is between 1 and 10 inclusive because of those min and max annotations.

What is the use of @validated annotation?

The @Validated annotation is a class-level annotation that we can use to tell Spring to validate parameters that are passed into a method of the annotated class. We'll learn more about how to use it in the section about validating path variables and request parameters.

What does @NotNull annotation Bean in Bean property?

@NotNull validates that the annotated property value is not null. @AssertTrue validates that the annotated property value is true.


2 Answers

I took a quick look at the BeanValidator API, and it looks like you might want to try the errorCodeConverter property.

You would need to implement your own ErrorCodeConverter, or use one of the provided implementations?

....
<bean id="validator" class="org.springmodules.validation.bean.BeanValidator"
    p:configurationLoader-ref="configurationLoader"
    p:errorCodeConverter-ref="errorCodeConverter" />

<bean id="errorCodeConverter" class="contact.MyErrorCodeConverter" />
....

Note: configurationLoader is another bean defined in the config XML used in the tutorial

Example converter:

package contact;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springmodules.validation.bean.converter.ErrorCodeConverter;

public class MyErrorCodeConverter implements ErrorCodeConverter {

    private Log log = LogFactory.getLog(MyErrorCodeConverter.class);

    @Override
    public String convertPropertyErrorCode(String errorCode, Class clazz, String property) {
        log.error(String.format("Property %s %s %s", errorCode, clazz.getClass().getName(), property));
        return errorCode;  // <------ use the errorCode only
    }

    @Override
    public String convertGlobalErrorCode(String errorCode, Class clazz) {
        log.error(String.format("Global %s %s", errorCode, clazz.getClass().getName()));
        return errorCode;
    }
}

Now the properties should work:

MyEmailErrorCode=Bad email

class Foo {
    @Email(errorCode="MyEmailErrorCode")
    String email
}
like image 200
toolkit Avatar answered Oct 04 '22 04:10

toolkit


Spring validation does have an ErrorCodeConverter that does this:

org.springmodules.validation.bean.converter.KeepAsIsErrorCodeConverter

When this is used, the resource bundle will be checked for the following codes:

[errorCode.commandBeanName.fieldName, errorCode.fieldName, errorCode.fieldClassName, errorCode]

  • errorCode is the actual validation errorCode eg. not.blank, email.
  • commandBeanName is the same as the model key name that references the form backing bean.
  • fieldName is the name of the field.
  • fieldClassName is the field class name eg. java.lang.String, java.lang.Integer

So for instance if I have a bean that is referenced in the model by the key "formBean" and the field emailAddress of type java.lang.String does not contain an email address, which causes the errorCode email. The validation framework will attempt to resolve the following message codes:

[email.formBean.emailAddress, email.emailAddress, email.java.lang.String, email]

If the errorCode is replaced by the errorCode "badEmail" like this:

@Email(errorCode="badEmail")

The messages codes that the framework will try resolve will be:

[badEmail.formBean.emailAddress, badEmail.emailAddress, badEmail.java.lang.String, badEmail]

I would suggest keeping the errodCode the same. Thus one message can be used for all fields that have that errorCode associated with them. If you need to be more specific with the message for a certain field you can add a message to the resource bundles with the code errorCode.commandBeanName.field.

like image 39
Rian Avatar answered Oct 04 '22 04:10

Rian