【问题标题】:Custom Messages in Bean Validation using Spring’s Validator interface使用 Spring 的 Validator 接口在 Bean Validation 中自定义消息
【发布时间】:2017-01-23 22:04:34
【问题描述】:

我正在使用带有 Rest Controller 的 Spring Boot 1.3.5,一切正常。

我还使用官方文档中的 Spring 验证示例技术(JSR-303 Bean Validation API 和 Spring 的验证器接口,我尝试了这两种方法并遇到了同样的问题)并且验证工作正常,但我无法配置自定义消息。

我已经配置了一个messages.properties 文件,我可以很好地访问这个文件中的消息。但是,此验证似乎无法读取或访问我通过 spring boot 自动配置的消息源(messages.properties)。

我可以直接从通过@Autowired 注入控制器的消息源对象访问消息(代码中有注释)。但是,Spring 的验证器接口或 JSR-303 Bean Validation 的绑定结果似乎无法访问加载在 MessageSource 中的 messages.properties。我得到的结果是我的错误有代码但没有默认消息。

这是我的应用程序类:

@SpringBootApplication
@ImportResource({ "classpath:security/cas-context.xml", "classpath:security/cas-integration.xml",
    "classpath:security/security.xml" })
@EnableAutoConfiguration(exclude = VelocityAutoConfiguration.class) // http://stackoverflow.com/questions/32067759/spring-boot-starter-cache-velocity-is-missing

public class Application extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(Application.class);
}

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}

@Bean
public ServletRegistrationBean cxfServlet() {
    return new ServletRegistrationBean(new CXFServlet(), "/services/*");
}

@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
    return new SpringBus();
}

@Bean
public Nfse nfseService() {
    return new NfseImpl();
}

@Bean
public Endpoint endpoint() {
    EndpointImpl endpoint = new EndpointImpl(springBus(), nfseService());
    endpoint.publish("/nfseSOAP");
    return endpoint;
}

}

这是我的 Bean:

public class Protocolo {

private Long id;

@NotNull
@Min(1)
@Max(1)
private String protocolo;

private StatusProtocoloEnum status;

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getProtocolo() {
    return protocolo;
}

public void setProtocolo(String protocolo) {
    this.protocolo = protocolo;
}

public StatusProtocoloEnum getStatus() {
    return status;
}

public void setStatus(StatusProtocoloEnum status) {
    this.status = status;
}

}

这是我的休息控制器:

@RestController
public class ProtocoloController {

@Autowired
private MessageSource messageSource;

@Autowired
private ProtocoloDAO protocoloDAO;

@RequestMapping(value = "/prot", method = RequestMethod.POST)
public void testar(@Valid @RequestBody Protocolo p) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    System.out.println(auth.getAuthorities());
    System.out.println(messageSource.getMessage("protocolo.tamanho", null, null)); 
// IN THIS PART I'M ABLE TO PRINT THE MESSAGE IF VALIDATION IS DISABLED
    System.out.println(p.getProtocolo());
}

}

所以,这段代码工作正常,并且没有调用该方法,因为我使用无效的 Protocolo 调用该方法。但是,我的 angularJS 客户端收到了填充了错误代码的响应,但所有默认消息都为空,因为验证没有看到我加载的 messages.properties。

有没有办法让我的 Spring 验证接口或 JSR-303 验证在 spring boot 中合并加载的 message.properties (messagesource)?我该如何纠正这个?如果有必要,我也可以粘贴我的 Spring Validation 接口代码示例。

非常感谢,

塔西西奥。

测试代码:

@RestController
public class ProtocoloController {

@Autowired
private MessageSource messageSource;

@Autowired
private ProtocoloDAO protocoloDAO;

@RequestMapping(value = "/prot", method = RequestMethod.POST)
public void testar(@Valid @RequestBody Protocolo p, BindingResult bindingResult) {
    System.out.println(messageSource.getMessage("Min.protocolo.protocolo", null, null));
    if (bindingResult.hasErrors()) {
               System.out.println(bindingResult.getFieldError().getDefaultMessage());
System.out.println(bindingResult.getFieldError().getCode());
    }
    System.out.println(p.getProtocolo());
}

}

【问题讨论】:

    标签: java spring spring-boot bean-validation spring-validator


    【解决方案1】:

    我在custom message in Spring validation 中解决了这个问题,阅读我答案的最后一部分。

    也检查this example

    我使用了带有自定义注释的自定义验证器。我需要更改自定义验证器中的代码。

    公共类 PersonValidator 实现 ConstraintValidator {

        @Override
        public boolean isValid(final Person person, final ConstraintValidatorContext context) { 
            if (somethingIsInvalid()) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("Something is invalid.").addConstraintViolation();
                return false;
            }
    
            return true;
        }
    }
    

    【讨论】:

      【解决方案2】:

      您应该在属性文件中有以下值:

      Min.protocolo.protocolo=Minimum value must be {1}
      

      然后在控制器中通过从 messageSource 对象调用函数 getMessage 来获取消息

      测试代码:

      @RequestMapping(value = "/prot", method = RequestMethod.POST)                        
      public void testar(@Valid @RequestBody Protocolo p, BindingResult bindingResult) {                        
      
          if (bindingResult.hasErrors()) {                        
              bindingResult.getFieldErrors().forEach(fieldError ->
                  System.out.println(messageSource.getMessage(fieldError, Locale.getDefault()))
              );
          }                        
          System.out.println(p.getProtocolo());                        
      }
      

      【讨论】:

        【解决方案3】:

        编辑: Spring Boot 1.5.3 中的已知错误参见https://github.com/spring-projects/spring-boot/issues/8979

        Spring Boot1.5.3 起您需要这样做

        @Configuration
        public class ValidationMessageConfig {
        
            @Bean
            public LocalValidatorFactoryBean mvcValidator(MessageSource messageSource) {
        
                LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
                factory.setValidationMessageSource(messageSource);
        
                return factory;
            }
        }
        

        然后它就会工作。

        对于1.5.2 及之前的版本,您可以扩展WebMVcConfigurerAdapter

        @Configuration
        public class ProfileMvcConfig extends WebMvcConfigurerAdapter {
        
            private MessageSource messageSource;
        
            @Autowired
            public ProfileMvcConfig(MessageSource messageSource) {
        
                this.messageSource = messageSource;
            }
        
            /**
             * This method is overridden due to use the {@link MessageSource message source} in bean validation.
             *
             * @return  A Validator using the {@link MessageSource message source} in bean validation.
             */
            @Override
            public Validator getValidator() {
        
                LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
                factory.setValidationMessageSource(messageSource);
        
                return factory;
            }
        }
        

        另见documentation

        【讨论】:

        • 这终于解决了我的问题。但是,即使我使用的是Spring boot 1.5.6.RELEASE,我也必须扩展WebMvcConfigurationSupport
        【解决方案4】:

        在 Spring Boot 应用程序中,MessageSource 配置了 MessageSourceAutoConfiguration,您不需要自动装配它。对于 jsr303,在 messages.properties 文件中创建正确的键值对。对于“protocolo”字段,您应该在属性文件中有以下值。

        NotNull.protocolo.protocolo=Field cannot be left blank
        Min.protocolo.protocolo=Minimum value must be {1}
        

        您还可以在代码中检查来自属性文件的消息,如下所示。

        public void testar(@Valid @RequestBody Protocolo p,BindingResult bindingResult) {
        if(bindingResult.hasErrors()) {
            System.out.println(bindingResult.getFieldError().getDefaultMessage());
            }
        }
        

        【讨论】:

        • 我已经试过了,但没有用。我做了自动接线只是为了测试我是否可以使用自动接线的消息源直接从控制器获取消息并且这是可能的。仅通过 JSR303 执行的验证,我无法收到消息。我在客户端收到您发布的这个确切的密钥:Min.protocolo.protocolo。但是,尽管在 messages.propeties 中指定了消息,但该消息为空。我想我缺少一些将验证 JSR303 或 spring 验证接口与 spring boot 配置的消息源连接起来的配置。
        • 我编辑了我的答案并为 bindingResult 添加了一些代码。你能检查一下吗?
        • 非常感谢!我使用您建议的代码编辑了我的帖子。我还将消息源保留为 autowired 仅用于测试。结果是springboot生成的消息源打印出来的消息,在控制器中自动装配的正是我在message.properties中自定义的消息:Min.protocolo.protocolo=my test。但是,绑定结果中打印的消息是 spring -JSR @Min 的默认消息,尽管代码消息(我也在测试中使用默认消息打印)与我通过自动装配消息源成功打印的消息相同。
        • @abaghel 我正在使用 Spring Boot 1.5.2 并且遇到相同的问题,即获取有效的错误代码但默认消息没有被覆盖。有什么解决办法吗??
        • @abaghel 这不起作用。我问了同样的问题并尝试了所有组合。您可以发布工作代码来证明这一点吗? stackoverflow.com/questions/49499891/…
        猜你喜欢
        • 1970-01-01
        • 2016-03-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-10
        • 2017-01-18
        • 2020-02-08
        • 1970-01-01
        相关资源
        最近更新 更多