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

custom validation message works for some errors

我有这个 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\main\webapp\WEB-INF\Validation\validation.properties

里面的内容是:

validate.user.yearsexperience = Only numbers can be used here.

validate.user.phone = Phone numbers can only be digits and should exactly be 10 digits.

validate.user.name.blank = Name cannot be left empty or Cannot contain more than 30 chars. It will accept letters only. No numbers or symbols are allowed either.

然后我设置了以下错误代码以预期 bean 验证器会捕获这些消息之一(但它没有)。

typeMismatch.user.yearsexperience = Only numbers can be used here.

typeMismatch.yearsexperience = Only numbers can be used here.

typeMismatch.java.lang.Integer = Only numbers can be used here.

typeMismatch = Only numbers can be used here.

因此,当我为 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));
}

这是我的 class 扩展 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 为什么自定义错误消息没有显示在一个字段中。但对于其他领域,它工作得很好。

Why the custom error doesn't show up for some fields but for others it works perfectly fine

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

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

您已经为 bean 验证正确配置了消息源并且它有效。

但是您正在自定义 typeMismatch 消息,看起来 Spring MVC 看不到这个。通常定义名称为 messageSource 的 bean 就足够了,但由于某些奇怪的原因,在您的情况下您还需要其他东西。

我的猜测是您有 2 个上下文,并且 messageSource 是在 Spring MVC 不可见的上下文中定义的。


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