7

Problem:

I am trying to use as much Spring Boot Auto-configuration as possible to reduce boiler plate code. I cannot get the Spring Validation codes to automatically map to my externalized messages.properties. It will only work if I add my own LocalValidatorFactoryBean and use the fully qualified javax constraint message.

Goal

I want to override the defaultMessage for @javax.validation.constraints.NotNull globally in my application based on Spring Validation codes.

The first thing I did was register this bean...do I really have to? I see Spring Boot has one but it doesn't seem to correlate to messages.properties.

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

The next one is surprising to me. Spring Validation has this nifty DefaultMessageCodesResolver that provides excellent code strategy for failures that look something like this:

"code": "NotNull",
"codes": [
   "NotNull.user.firstName",
   "NotNull.firstName",
   "NotNull.java.lang.String",
   "NotNull"
],

But these codes aren't even considered for the @NotNull constraint. The spring documentation for Validation Conversion hints that something like this does exist but falls short of anything useful yet.

I wrote a sample application here and you can see that it's not working.

Question

How can I override the default message of Spring Validation errors based on those codes from messages.properties?

Relevant build.gradle

  • springBootVersion = '2.0.0.RELEASE'
  • compile('org.springframework.boot:spring-boot-starter-web')

src/main/resources/messages.properties:

NotNull.user.firstName=This doesn't work
NotNull.firstName=Or this
NotNull.java.lang.String=This doesn't work either
NotNull=And this doesn't work

javax.validation.constraints.NotNull.message=THIS DOES WORK! But why have codes then?

Spring Auto-Configuration Output proving the auto-config loaded.

   MessageSourceAutoConfiguration matched:
      - ResourceBundle found bundle URL [file:/home/szgaljic/git/jg-github/journey-through-spring/basic-web-validations/build/resources/main/messages.properties] (MessageSourceAutoConfiguration.ResourceBundleCondition)
      - @ConditionalOnMissingBean (types: org.springframework.context.MessageSource; SearchStrategy: current) did not find any beans (OnBeanCondition)

Some other relevant start-up logs:

2018-03-26 14:32:22.970 TRACE 1093 --- [           main] o.s.beans.CachedIntrospectionResults     : Found bean property 'validationMessageSource' of type [org.springframework.context.MessageSource]

Validation Constraint:

public class User {
    @NotNull
    private String username;

    @NotNull
    private String firstName;
}

Controller just for Testing:

@RestController
public class UserController {

    @PostMapping("/users")
    public List<ObjectError> testValidation(@Valid @RequestBody User user){
       return null; // null implies no failed validations.. just for testing
    }

}

Failed to use the codes

I tried to comment/uncomment the messages in messages.properties but it will only take the value of javax.validation.constraints.NotNull.message.

szxnyc
  • 2,495
  • 5
  • 35
  • 46
  • Maybe [this](https://stackoverflow.com/questions/45692179/spring-boot-validation-message-is-not-being-resolved?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) can help? – ailav Mar 27 '18 at 06:43
  • 1
    @ailav thanks, I'm trying to have minimal code and was hoping not to even need to register LocalValidatorFactoryBean. But even with that it doesn't care about the codes. I'll update my question with this added for more clarity. – szxnyc Mar 27 '18 at 14:25

1 Answers1

1

Because of the configuration

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

So you're using Bean Validation then spring get the messages from message.properties file that override from org.hibernate.validator.ValidationMessages.properties file using the key javax.validation.constraints.NotNull.message. If you would like to have key-value pairs:

NotNull.user.firstName=This doesn't work
NotNull.firstName=Or this
NotNull.java.lang.String=This doesn't work either
NotNull=And this doesn't work

to work you have to write as follows:

@Autowired
private MessageSource messageSource;

@PostMapping("/users")
public List<ObjectError> testValidation(@Valid @RequestBody User user, BindingResult bindingResult){
   bindingResult.getFieldErrors().forEach(fieldError -> new ObjectError(
   /*Assumes that ObjectError has constructor of ObjectError(String message) */
          messageSource.getMessage(fieldError, Locale.getDefault())
   ));
   return null; 
}
Lê văn Huy
  • 361
  • 3
  • 7