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

标签 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\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 validator 捕获这些消息之一(但它没有)。

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字段,它显示自己的typeMismatch无法将String转换为Integer等异常

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

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 异常。

此时,我认为表单绑定(bind)可能存在问题(我不应该这样认为,因为 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) 添加自定义 validator ,如下所示:

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

这就是我的自定义 validator 的外观,

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
  • 将用户值从 a 绑定(bind)到 POJO 类的 spring-mvc

您已经正确配置了 Bean 验证的消息源并且它可以工作。

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

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

<小时/>

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

关于java - 自定义验证消息适用于某些错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43479640/

相关文章:

java - spring mvc拦截器如何获取方法执行流程

java - Spring-cglib 代理类层次结构中的行为

java - HibernateDaoSupport ,事务不回滚

java - 仅限 Spring Security SessionRegistry java 配置

java - 使用jpcap伪造ARPRequest

java - 为什么 libGDX 计时器无法按照我的方法运行?

java - 如何仅使用一个 HTTP 请求(JSON 文件)实例化 2 个不同的对象

java - 如何创建一个 Spring Data JPA 查询,该查询将从相同类型的对象列表中返回与 3 个参数匹配的对象

java - 同一 Controller 的多个 Spring MVC validator

java - 如何管理由于发布请求和历史记录而产生的 session 属性