自定义验证消息适用于某些错误

时间:2017-04-18 18:27:19

标签: java spring spring-mvc spring-validator

我有这个Web应用程序,我已经设置了bean验证,它的工作非常好。但对于一个字段,它不会读取我在validation.properties文件中设置的消息。对于其他领域,它的工作原理。我很困惑为什么会这样?让我告诉你我的设置:

@Bean
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setCacheSeconds(0);
    messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
    messageSource.setBasenames("/WEB-INF/Validation/validation"); //I have tried setBaseName as well but same difference.
    return messageSource;
}

@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
    LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
    validator.setValidationMessageSource(this.messageSource());
    return validator;
}

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
    processor.setValidator(this.localValidatorFactoryBean());
    return processor;
}

@Autowired
SpringValidatorAdapter validator; //global validator

@Override
public Validator getValidator() {
    return this.validator;
}

我的属性文件位于正确的位置。我知道,因为有些错误,会从此validation.properties文件中读取消息。位置是:

  

SRC \主\ web应用\ WEB-INF \验证\ validation.properties

其中的内容是:

  

validate.user.yearsexperience =这里只能使用数字。

     

validate.user.phone =电话号码只能是数字,应该是10位数。

     

validate.user.name.blank =名称不能为空或不能包含超过30个字符。它只接受信件。也不允许使用数字或符号。

然后我设置了以下错误代码,期待bean验证程序捕获其中一条消息(但它没有)。

  

typeMismatch.user.yearsexperience =这里只能使用数字。

     

typeMismatch.yearsexperience =这里只能使用数字。

     

typeMismatch.java.lang.Integer =这里只能使用数字。

     

typeMismatch =此处只能使用数字。

因此,当我为phonename字段输入错误数据时,JSP会显示我在validation.properties文件中设置的那些消息,但对于yearsexperience字段,则显示显示自己的typeMismatchcannot convert String to Integer, etc exceptions

这里让我向您展示我在模型中使用的注释:

public class User{
    @NotBlankNoFancyLettersOrNumbers(message = "{validate.user.name.blank}") //My own custom annotation. Works like a charm and when user enters wrong data, it shows my custom error. 
    private String name;
    @Pattern(regexp="(^$|[0-9]{10})", message = "{validate.user.phone}") //javax's builtin validation constraint
    private String phone;
    @So many things I have tried. But first I started with @Pattern
    private Integer yearsexperience;

对于此Integer字段,我无法使用@Pattern,因为它只能放在String字段上(我在这里是否正确?)。

然后我尝试@Digits。当用户输入错误的输入时,它确实有效,但没有显示我的错误消息,它向用户显示typeMismatch例外。然后我制作了自己的自定义注释,并为其命名@DigitsOnly。同样的问题,它不会显示我的错误信息。它显示了相同的旧typeMismatch异常。

此时,我认为表单绑定可能存在问题(我应该认为因为bean验证适用于其他字段)。无论如何,我注册了我自己的自定义编辑器:

@InitBinder("user")
protected void binder(WebDataBinder binder, HttpSession session) throws Exception {     
    binder.registerCustomEditor(Date.class, new DateConvertor()); //This is unrelated to my question but it works!      
    binder.registerCustomEditor(Integer.class, "yearsexperience", new IntegerConvertor(session.getAttribute("user"), this.userdao));
}

这是我的课程PropertyEditorSupport

public class IntegerConvertor extends PropertyEditorSupport {   
private User user;  
private UserDAO userdao;
public IntegerConvertor(){}

public IntegerConvertor(Object object, UserDAO userdao) {
    if (object instanceof User){            
        this.user= new User((User) object);
    }
    this.userdao = userdao;
}       

@Override
public void setAsText(String value) {       
if (!value.matches("[0-9]+")) { //if incoming value DOES NOT contains numbers only      
setValue(this.userdao.getYearsExperienceBeforeChange(this.user.getId()));
} else {            
setValue(value);
}       
}

现在,我这样做是为了希望typeMismatch异常将显示给用户,但它仍会显示异常。 (我从这个PropertyEditorSupport知道,或者它不会从validation.properties文件中读取自定义错误消息。

无论如何,我尝试通过这样的WebDataBinder.addValidator(myCustomValidator)添加自定义验证器:

 @InitBinder("user")
protected void binder(WebDataBinder binder, HttpSession session) throws Exception {     
    binder.registerCustomEditor(Date.class, new DateConvertor());       
    binder.addValidators(new myCustormValidator());
}

这是我的自定义验证器的外观,

public class MyCustormValidator implements Validator {  

@Override
public boolean supports(Class<?> clazz) {
    return User.class.equals(clazz); //return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {

    User user = (User) target;  
    String a = Integer.toString(user.getYearsExperience());     
    if(!a.matches("[0-9]+")){
        errors.rejectValue("yearsexperience","validate.user.yearsexperience");
    } 
  }
}

我将Integer转换为String因为我能够检查传入的值是否只包含数字而不是字母。但在这里它会导致另一个问题:它不显示传入值,它显示旧值。而且由于旧的价值已经是正确的,它不会引起任何问题。所以我添加了else语句,只是为了抛出一个错误,希望现在至少它会显示我的自定义错误。但事实并非如此!

因此,我来​​到这里寻求帮助。非常感谢任何帮助。

TL; DR 为什么自定义错误消息不会显示在一个字段中。但对于其他领域,它的工作非常好。

1 个答案:

答案 0 :(得分:1)

  

为什么自定义错误不会出现在某些字段中,但对于其他字段则可以正常显示

因为涉及两个不同的组件:

  • 用于bean验证的hibernate-validator
  • spring-mvc,用于将用户值从a绑定到POJO类

您已正确配置了bean验证的消息源,但它确实有效。

但是你要自定义typeMismatch消息,看起来像Spring MVC看不到这一点。通常,它足以定义名为messageSource的bean,但出于某种奇怪的原因,您需要其他东西。

我的猜测是你有两个上下文,messageSource是在Spring MVC不可见的上下文中定义的。

<强>更新 我查看了GitHub上的示例项目(http://github.com/farazdurrani/sscce),我发现非工作typeMismatch消息的问题不是因为配置问题。之所以会发生这种情况,是因为您未使用标准代码并尝试手动显示错误消息(通过对来自getDefaultMessage()的每个错误调用Errors.getAllErrors()方法)。如果您尝试使用<form:errors path="*"/>,则会发现所有错误消息都已正确本地化。