Spring验证错误生成

时间:2010-11-18 21:25:21

标签: java spring spring-mvc

我正在使用JSR-303进行验证和Spring MVC 3.验证是通过控制器中的validator.validate(bean, errors)手动完成的。

我使用name注释了Foo类的@NotNull属性。验证失败时,我注意到Spring MVC在Errors对象中生成了以下错误代码。

NotNull.form.foo.name
NotNull.name
NotNull.java.lang.String
NotNull

<form:errors>标记的工作方式是它将遍历所有错误代码并查找默认资源包,直到它返回非空消息。有没有办法自定义这些错误代码或至少列出它们的顺序?

原因是我有一个自定义ResourceBundle对象,如果在资源包文本文件中找不到,则返回从给定消息代码派生的默认消息。由于标记在错误代码列表中向下运行,因此在此示例中它将首先查找NotNull.form.foo.name。文本文件当然没有此条目,因此自定义ResourceBundle对象将返回默认消息。问题是,我已经在NotNull的文本文件中定义了一条消息,但标签会看到它的原样。

如果我能以某种方式只生成一个错误代码,或者反转错误代码的顺序,那就可以了。

有什么想法吗?感谢。

2 个答案:

答案 0 :(得分:2)

我使用以下类使用Spring验证器手动执行JSR-303和HibernateValidator bean验证。也许它可能有用。

<强> BeanValidator.java

import java.util.Locale;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;

public class BeanValidator implements org.springframework.validation.Validator, InitializingBean {

private static final Logger log = LoggerFactory.getLogger(BeanValidator.class.getName());

private Validator validator;

@Autowired
MessageSource messageSource;

@Override
public void afterPropertiesSet() throws Exception {
    ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
    validator = validatorFactory.usingContext().getValidator();
}

@Override
public boolean supports(Class clazz) {
    return true;
}

@Override
public void validate(Object target, Errors errors) {
    Set<ConstraintViolation<Object>> constraintViolations = validator.validate(target);
    for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
        String propertyPath = constraintViolation.getPropertyPath().toString();
        String message;
        try {
            message = messageSource.getMessage(constraintViolation.getMessage(), new Object[]{}, Locale.getDefault());
        } catch (NoSuchMessageException e) {
            log.error(String.format("Could not interpolate message \"%s\" for validator. "
                    + e.getMessage(), constraintViolation.getMessage()), e);
            message = constraintViolation.getMessage();
        }
        errors.rejectValue(propertyPath, "", message);
    }
}
}

<强> SpringMessageSourceMessageInterpolator.java

import javax.validation.MessageInterpolator;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.NoSuchMessageException;

public class SpringMessageSourceMessageInterpolator extends ResourceBundleMessageInterpolator implements MessageInterpolator, MessageSourceAware, InitializingBean {

@Autowired
private MessageSource messageSource;

@Override
public String interpolate(String messageTemplate, Context context) {
    try {
        return messageSource.getMessage(messageTemplate, new Object[]{}, Locale.getDefault());
    } catch (NoSuchMessageException e) {
        return super.interpolate(messageTemplate, context);
    }
}

@Override
public String interpolate(String messageTemplate, Context context, Locale locale) {
    try {
        return messageSource.getMessage(messageTemplate, new Object[]{}, locale);
    } catch (NoSuchMessageException e) {
        return super.interpolate(messageTemplate, context, locale);
    }
}

@Override
public void setMessageSource(MessageSource messageSource) {
    this.messageSource = messageSource;
}

@Override
public void afterPropertiesSet() throws Exception {
    if (messageSource == null) {
        throw new IllegalStateException("MessageSource was not injected, could not initialize "
                + this.getClass().getSimpleName());
    }
}
}

<强>的applicationContext.xml

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="messages"/>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="messageInterpolator">
        <bean class="com.company.utils.spring.SpringMessageSourceMessageInterpolator" />
    </property>
</bean>

示例bean属性

@NotNull(message = "validation.mandatoryField")
private ClientGroup clientGroup;

验证示例

@Controller
public class MyController {

    @Autowired
    private BeanValidator validator;

    @RequestMapping("/foo", method=RequestMethod.POST)
    public void processFoo(ModelAttribute("foo") Foo foo, BindingResult result, Model model) {
        //...
        validator.validate(foo, result);
    }
}

答案 1 :(得分:2)

找不到比这更简单的解决方案...

我-servlet.xml中

<bean id="handlerAdapter" class="org.opensource.web.StandardAnnotationMethodHandlerAdapter">
</bean>

<强> StandardAnnotationMethodHandlerAdapter.java

public class StandardAnnotationMethodHandlerAdapter extends AnnotationMethodHandlerAdapter    {
    @Override
    protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName) throws Exception {
    MyServletRequestDataBinder dataBinder = new MyServletRequestDataBinder(target, objectName);
    return dataBinder;
   }
}

MyServletRequestDataBinder .java

public class MyServletRequestDataBinder extends ServletRequestDataBinder {

private MessageCodesResolver messageCodesResolver = new MyMessageCodesResolver();

@Override
public void initBeanPropertyAccess() {
   super.initBeanPropertyAccess();
   BindingResult bindingResult = super.getBindingResult();
   if(bindingResult instanceof AbstractBindingResult) {
       ((AbstractBindingResult)bindingResult).setMessageCodesResolver(messageCodesResolver);
   }
}

@Override
public void initDirectFieldAccess() {
  super.initDirectFieldAccess();
  BindingResult bindingResult = super.getBindingResult();
  if(bindingResult instanceof AbstractBindingResult) {
     ((AbstractBindingResult)bindingResult).setMessageCodesResolver(messageCodesResolver);
    }
 }
}

MyMessageCodesResolver .java

public class MyMessageCodesResolver extends DefaultMessageCodesResolver {

public static final String NOT_NULL_ERROR_CODE = "NotNull";

@Override
public String[] resolveMessageCodes(String errorCode, String objectName, String field, Class fieldType) {
   if(NOT_NULL_ERROR_CODE.equalsIgnoreCase(errorCode)) {
       String notNullErrorCode = errorCode + CODE_SEPARATOR + objectName + CODE_SEPARATOR + field;
       //notNullErrorCode = postProcessMessageCode(notNullErrorCode);
       return new String[] {notNullErrorCode};
    }
      return super.resolveMessageCodes(errorCode, objectName, field, fieldType);
}   
}