1

I'm using Spring Boot 1.3.5 with Rest Controllers and everything is working fine.

I am also using Spring's validation sample techniques from the official documentation (JSR-303 Bean Validation API and Spring's validator interface, i tried both and faced the same problem) and the validations are working, but I am not able to configure custom messages.

I have configured a messages.properties file, and I can access the messages on this file just fine. However this validation seems not to be capable of reading or accessing my messages source (messages.properties) configured automatically via spring boot.

I can access the messages directly from the messages source object injected in controller via @Autowired (there's a comment in the code). However, the binding result of the Spring's validator interface or the JSR-303 Bean Validation seems to not be capable of accessing the messages.properties loaded in MessageSource. The result I have is that my errors have codes but don't have default messages.

Here is my Application class:

@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;
}

}

Here is my 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;
}

}

Here is My rest controller:

@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());
}

}

So, this code works fine and the method is not called since i'm calling the method with a invalid Protocolo. However, my angularJS client receives the response with the errors codes populated but with all the default messages empty since the validation is not seeing my loaded messages.properties.

Is there a way to make my Spring validation Interfaces or JSR-303 validation incorporate the loaded message.properties (messagesource) in spring boot ? How can i correct this ? If it's necessary i can paste my code sample of Spring Validation interfaces also.

Thank's a lot,

Tarcísio.

TEST CODE:

@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());
}

}

Tarcísio Filó
  • 11
  • 1
  • 1
  • 3

4 Answers4

6

Edit: Known Bug in Spring Boot 1.5.3 see https://github.com/spring-projects/spring-boot/issues/8979

In Spring Boot since 1.5.3 you need to do this

@Configuration
public class ValidationMessageConfig {

    @Bean
    public LocalValidatorFactoryBean mvcValidator(MessageSource messageSource) {

        LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
        factory.setValidationMessageSource(messageSource);

        return factory;
    }
}

and then it will work.

With version 1.5.2 and before you can extend 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;
    }
}

also see the documentation

Tobsch
  • 175
  • 2
  • 10
  • This finally fixed my issue. However I also had to extend `WebMvcConfigurationSupport` even though i am using `Spring boot 1.5.6.RELEASE` – Abdullah Khan Mar 04 '19 at 13:58
1

In Spring Boot applicaton MessageSource is configured with a MessageSourceAutoConfiguration and you don't need to autowire it. For jsr303, create proper key-value pair in the messages.properties file. For "protocolo" field, you should have following values in property file.

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

You can also check messages from property file like below in your code.

public void testar(@Valid @RequestBody Protocolo p,BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
    System.out.println(bindingResult.getFieldError().getDefaultMessage());
    }
}
abaghel
  • 14,783
  • 2
  • 50
  • 66
  • 1
    I already tried it and it didn't work. I did the autowire just for test if i could get the message directly from the controller with the autowired messagessource and it was possible. Just with the validation executed by JSR303 I could not get the message. I receive in client this exact key that you've posted: Min.protocolo.protocolo. However, the message is null although it is specified in messages.propeties. I thing i'm missing some configuration that connects the validation JSR303 or the spring validation interface with the messagesource configured by spring boot. – Tarcísio Filó Sep 16 '16 at 13:12
  • I edited my answer and added some code for bindingResult. Can you please check using that? – abaghel Sep 16 '16 at 13:20
  • 1
    Thanks a lot ! I edited my post with the code that you've suggested. I also kept the message source with autowired just for test. The result was that the message printed with the message source generated by springboot and autowired in the controller was exactly the message that i customized in message.properties: Min.protocolo.protocolo=my test. However, the message printed in the binding result was the default message of spring -JSR @Min, althougth the code message (that i also printed in the test with the default message) was the same that i printed with success via autowired message source. – Tarcísio Filó Sep 16 '16 at 15:31
  • @abaghel I am using Spring Boot 1.5.2 and having the same issue i.e. getting the valid error codes but default message is not being overridden. Any work around for this?? – Yogen Rai Apr 18 '17 at 16:10
  • @abaghel this does not work. I asked this same question and tried all combinations. Can you post working code to prove this? https://stackoverflow.com/questions/49499891/spring-boot-mapping-validation-codes-to-messagesource-message – szxnyc Mar 27 '18 at 02:49
0

you should have following values in property file:

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

then in the controller you obtain the message by calling function getMessage from messageSource object

Test code:

@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());                        
}
Lê văn Huy
  • 361
  • 3
  • 7
0

I solved this in custom message in Spring validation read the last part of my answer.

Check this example as well.

I used a custom validator with custom annotation. I needed to change code in my custom validator.

public class PersonValidator implements 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;
    }
}
Yan Khonski
  • 12,225
  • 15
  • 76
  • 114