Search code examples
springhibernatespring-mvchibernate-validator

Spring 4 & Hibernate 5 validator messageSource does not work


I can't get use to work hibernate validation 5.1.0.Final with Spring MVC 4.2.5.RELEASE.

My WebConfig:

    @Bean
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.setValidationMessageSource(messageSource());
        return validator;
    }
    @Bean
    @Autowired
    public MethodValidationPostProcessor getValidationPostProcessor(LocalValidatorFactoryBean validator) {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        processor.setValidator(validator);
        return processor;
    }

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("i18n/messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setUseCodeAsDefaultMessage(true);
        return messageSource;
    }

    @Override
    public Validator getValidator() {
        return validator();
    }
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor(){
        LocaleChangeInterceptor l = new LocaleChangeInterceptor();
        l.setParamName("lang");
        return l;
    }

    @Bean
    public SessionLocaleResolver localeResolver(){
        SessionLocaleResolver s = new SessionLocaleResolver();
        s.setDefaultLocale(Locale.ENGLISH);
        return s;
    }

I have an ExceptionHanlder which gets validation messages and push it back as a json:

    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ValidationError handleConstraintViolation(final ConstraintViolationException exception) {
        ValidationError v = new ValidationError();

        exception.getConstraintViolations().forEach(violation -> {
                v.addError(violation.getPropertyPath().toString(), violation.getMessage());
        });
        logger.warn(exception, exception);
        return v;
    }

I have 3 files in src/main/resources/i18n/: messages_en_EN.properties, messages_pl_PL.properties, messages.properties. My model class with validation has one validated parameter:

    @Column(name = "VALUE_", nullable = false)
    @Email(message = "{Email.contractorContactEmail.value}")
    @NotNull(message = "{NotNull.contractorContactEmail.value}")
    private String value;

What I see is that hibernate validator look into classpath:ValidationMessages properties not into my spring message source. It may be ok for me but Hibernate does not want to translate those messages - locale is always server default. What am I doing wrong?? How can I fix it?

PS. In controller I use @org.springframework.validation.annotation.Validated.

PS2. I am sure that my messageSource is working correctly because if I add this code into ExceptionHandler it translates perfectly but I know that it is bad practice.

exception.getConstraintViolations().forEach(violation -> {
            String messageTemplate = violation.getMessageTemplate();
            if (messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {
                String key = StringUtils.substring(messageTemplate, 1, messageTemplate.length() - 1);
                v.addError(violation.getPropertyPath().toString(), messageSource.getMessage(key, null, LocaleContextHolder.getLocale()));
            }
});

Solution

  • What I can tell you right away, judging from your configuration, your messages are in the wrong place - you've configured the message source to look at i18/messages but your messages are in the classpath root. So, your message source definition should look like:

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setUseCodeAsDefaultMessage(true);
        return messageSource;
    }
    

    Aside from that, the rest of the configuration looks pretty much okay. I'm guessing that you're using the WebMvcConfigurerAdapter class?

    EDIT: For future reader's reference, the issue was not configuration-related, JPA entity was missing a @Valid annotation on a @OneToMany field so controller-level validation failed to pick it up and Hibernate's JPA validator used default messages.