Hibernate validation message from properties - spring

I am trying to get a custom message from property file, but I can't.
My configs:
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/messages/ValidationMessages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setFallbackToSystemLocale(true);
return messageSource;
}
Class which must be valid:
public class NewUserDto {
#NotEmpty(message = "{NotEmpty.newUserDto.email}")
private String email;
}
Value in my property file: NotEmpty.newUserDto.email = Some value.
But instead Some value I get {NotEmpty.newUserDto.email}, why it is so?

If you are using java configuration. You can refer to the below example:
Delcare a bean like this
public class Employee implements Serializable {
#Email #NotEmpty
private String email;
}
A custom message is located in a file messages.properties
Email.employee.email=Please provide a valid Email address
and this file is added in WebMvcConfigurerAdapter
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
I found this example from the post Spring MVC Form Validation Annotation Example

If I understood correctly, you are trying to inject a String message from properties file, which in it, you have an entry of:
NotEmpty.newUserDto.email
And it got a value that should act as your message error in case of bean validation failure.
In this case:
First, use expression language to inject the property. like this:
#Component
public class NewUserDto {
#Value("${NotEmpty.newUserDto.email}")
private String msg;
#NotEmpty(message = msg)
private String email;
}
Don't forget to annotate your class with #Component annotation and turn it to bean in order to do this #Value injection. of course, you'll have to add to your configuration file the next:
<context:component-scan base-package="package.to.NewUserDto" />
You can try to do it directly, like this:
#Component
public class NewUserDto {
#NotEmpty(message = #Value("${NotEmpty.newUserDto.email}"))
private String email;
}

Related

Why I'm getting org.springframework.context.NoSuchMessageException in spring boot?

I'm having properties/messages file name:
messages_en.properties
And I have the property inside:
country.AF.name=Afghanistan
here is the messages class:
#ApplicationScope
#Configuration
#Slf4j
public class Messages {
private ResourceBundleMessageSource messageSource;
private MessageSourceAccessor accessor;
#PostConstruct
private void init() {
messageSource();
accessor = new MessageSourceAccessor(messageSource, Locale.ENGLISH);
log.info("Messages initialized");
}
public String get(String code) {
return accessor.getMessage(code);
}
public void messageSource() {
messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("classpath:messages_en.properties");
messageSource.setUseCodeAsDefaultMessage(true);
}
The full error I'm getting is as follow:
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.3.jar:2.12.3]
Postman show wrong results, I need to get all the countries:
Did you add the below things message Sources bean configuration
messageSource
.setBasename("classpath:messages_en.properties")

Springboot error message interpolation not working for custom validator

I mixed both org.springframework.validation together with JSR-303 annotation.
JSR-303 annotation:
public class Model{
private String type;
private State state;
#NotNull(message = "{comment.notnull}")
private String comment;
}
Spring framework validation:
#Component
public class ModelValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Model.class.equals(clazz);
}
#Override
public void validate(Object obj, Errors errors) {
Model model = (Model) obj;
if (eventModel.getState() == null) {
errors.reject("state", "error.state.invalidState");
}
}
}
My ValidationMessages_en.properties
error.state.invalidState=Invalid state.
comment.notnull=Comment not null.
Then my configuration:
#Configuration
public class ValidationConfig {
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("ValidationMessages");
return messageSource;
}
}
When i run my springboot application, the interpolation works for JSR-303, but not the custom validator, did i miss something? Tried for long time but can't figure out.
Result:
"error_messages": [
{
"error_message": "Comment not null"
},
{
"error_message": "error.state.invalidState"
}
]
I am not sure if there is any easy way but eventually I had to do like this ,
Added below component ,
import java.util.Locale;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.stereotype.Component;
#Component
public class Messages {
#Autowired
private MessageSource messageSource;
private MessageSourceAccessor accessor;
#PostConstruct
private void init() {
accessor = new MessageSourceAccessor(messageSource, Locale.ENGLISH);
}
public String get(String property) {
return accessor.getMessage(property);
}
}
Then I would inject this component in my validator & use above get("error.state.invalidState") method of this class instead of error.state.invalidState directly.
messageSource is the bean that you already defined in system. Locale can be externalized and default locale can be set with another configuration.
MessageSourceAccessor has lots of overloaded methods, you can directly expose that too if wish to provide those options.
I think you have confused between two methods:
reject(String errorCode, String defaultMessage)
rejectValue(String field, String errorCode)
As your configuration seems fine, just doing the following should solve your problem:
errors.rejectValue("state", "error.state.invalidState");
Or
errors.reject("error.state.invalidState", "Some default message to fallback!");

Spring, inject properties in a Bean instance based one of that Bean's field value, is it possible?

I have a Pojo I use to configure webservices' clients:
public class ServiceConfig {
private String url;
private String endpoint;
private String serviceName;
public ServiceConfig(String serviceName) {
super();
this.serviceName = serviceName;
}
}
Now, this is what my application.properties file looks like:
service1.url=http://localhost:8087/
service1.endpoint=SOME_ENDPOIT1
service2.url=http://localhost:8085/
service2.endpoint=SOME_ENDPOIT2
service3.url=http://localhost:8086/
service3.endpoint=SOME_ENDPOIT3
service4.url=http://localhost:8088/
service4.endpoint=SOME_ENDPOIT4
What I'm trying to achieve is for Spring to inject the correct properties when I instantiate ServiceConfig like this:
ServiceConfig sc = new ServiceConfig("service1");
Is it possible?
Are you using just spring or also spring-boot?
What about injecting org.springframework.core.env.Environment to your pojo and configuring it with it.
so something like this could work:
public class ServiceConfig {
private String url;
private String endpoint;
private String serviceName;
public ServiceConfig(String serviceName, Environment env) {
// TODO assert on serviceName not empty
this.serviceName = serviceName;
this.url = env.getProperty(serviceName.concat(".url");
this.endpoint = env.getProperty(serviceName.concat(".endpoint");
}
}
I guess there could be a simpler/more elegant solution, but I don't know your case.
spring-boot version
well with spring boot just define your pojo (field names must match property names)
public class ServiceConfig {
private String url;
private String endpoint;
// getters setters
}
and then in some configuration you can do this (note: value in ConfigurationProperties is prefix of your configuration in application.properties):
#Configuration
public class ServicesConfiguration {
#Bean
#ConfigurationProperties("service1")
ServiceConfig service1(){
return new ServiceConfig();
}
#Bean
#ConfigurationProperties("service2")
ServiceConfig service2(){
return new ServiceConfig();
}
}

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

Spring MockMvc Passing Nested Form Parameters

I have the following form
public class MyForm {
private Account account;
}
public class Account {
private String firstName;
}
How do I pass firstName parameter?
(The following approach does not work)
mockMvc.perform(post("/xyz")
.param("account.firstName", "John"))
.andExpect(model().hasErrors())
.andExpect(view().name("/xyz"))
.andExpect(status().isOk())
Finally I resolved this issue. Since I am using standalone setup I had to define validator and messagesource.
void setupTest() {
MockitoAnnotations.initMocks(this)
this.mockMvc = MockMvcBuilders.standaloneSetup(getController())
.setValidator(getValidator())
.alwaysDo(MockMvcResultHandlers.print())
.build()
}
private MessageSource getMessageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
private LocalValidatorFactoryBean getValidator() {
def validator = new LocalValidatorFactoryBean()
validator.setValidationMessageSource(getMessageSource());
return validator;
}

Resources