Reputation: 6285
I have this web application where I have setup bean validation and it works fantastically well. But for one field, it doesn't read the message that I have setup in my validation.properties file. For other fields, it works. I am perplexed as to why this happens? Let me show you my setup:
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setCacheSeconds(0);
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
messageSource.setBasenames("/WEB-INF/Validation/validation"); //I have tried setBaseName as well but same difference.
return messageSource;
}
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(this.messageSource());
return validator;
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(this.localValidatorFactoryBean());
return processor;
}
@Autowired
SpringValidatorAdapter validator; //global validator
@Override
public Validator getValidator() {
return this.validator;
}
My properties file is in correct location. I know that because for some errors, messages are read from this validation.properties file. Location is:
src\main\webapp\WEB-INF\Validation\validation.properties
Content inside it is:
validate.user.yearsexperience = Only numbers can be used here.
validate.user.phone = Phone numbers can only be digits and should exactly be 10 digits.
validate.user.name.blank = Name cannot be left empty or Cannot contain more than 30 chars. It will accept letters only. No numbers or symbols are allowed either.
Then I setup following error codes in anticipation of bean validator catching one of these messages (but it did not).
typeMismatch.user.yearsexperience = Only numbers can be used here.
typeMismatch.yearsexperience = Only numbers can be used here.
typeMismatch.java.lang.Integer = Only numbers can be used here.
typeMismatch = Only numbers can be used here.
So when I enter wrong data for phone
or name
field, JSP shows those messages that I have set in my validation.properties
file, but for yearsexperience
field, it shows its own typeMismatch
or cannot convert String to Integer, etc exceptions
.
Here let me show you what Annotations I used over my model:
public class User{
@NotBlankNoFancyLettersOrNumbers(message = "{validate.user.name.blank}") //My own custom annotation. Works like a charm and when user enters wrong data, it shows my custom error.
private String name;
@Pattern(regexp="(^$|[0-9]{10})", message = "{validate.user.phone}") //javax's builtin validation constraint
private String phone;
@So many things I have tried. But first I started with @Pattern
private Integer yearsexperience;
For this Integer
field, I cannot use @Pattern
because it can only be put over String
fields (am I correct here?).
Then I tried @Digits
. When a user enters wrong input, it sure works, but doesn't show my error message, it shows a typeMismatch
exception to the user. Then I made my own custom annotation and I gave it a name @DigitsOnly
. Same issue, it would't show my error message. It shows the same old typeMismatch
exception.
At this point, I thought maybe there is a problem with form binding (I should not have thought that because bean validation works properly for other fields). Anyway, I registered my own custom editor like this:
@InitBinder("user")
protected void binder(WebDataBinder binder, HttpSession session) throws Exception {
binder.registerCustomEditor(Date.class, new DateConvertor()); //This is unrelated to my question but it works!
binder.registerCustomEditor(Integer.class, "yearsexperience", new IntegerConvertor(session.getAttribute("user"), this.userdao));
}
This is my class that extends PropertyEditorSupport
:
public class IntegerConvertor extends PropertyEditorSupport {
private User user;
private UserDAO userdao;
public IntegerConvertor(){}
public IntegerConvertor(Object object, UserDAO userdao) {
if (object instanceof User){
this.user= new User((User) object);
}
this.userdao = userdao;
}
@Override
public void setAsText(String value) {
if (!value.matches("[0-9]+")) { //if incoming value DOES NOT contains numbers only
setValue(this.userdao.getYearsExperienceBeforeChange(this.user.getId()));
} else {
setValue(value);
}
}
Now, I did this in a hope that typeMismatch
exception will not be shown to the user, but still it shows that exception. (I knew that from this PropertyEditorSupport
, or it wouldn't read custom error message from validation.properties
file).
Anyway, then I tried to add a custom validator thru WebDataBinder.addValidator(myCustomValidator)
like this:
@InitBinder("user")
protected void binder(WebDataBinder binder, HttpSession session) throws Exception {
binder.registerCustomEditor(Date.class, new DateConvertor());
binder.addValidators(new myCustormValidator());
}
This is how my custom validator looks,
public class MyCustormValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.equals(clazz); //return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
String a = Integer.toString(user.getYearsExperience());
if(!a.matches("[0-9]+")){
errors.rejectValue("yearsexperience","validate.user.yearsexperience");
}
}
}
I converted that Integer
to String
because then I was able to check if the incoming value contains digits only and not alphabets. But here it causes another issue: it doesn't show the incoming value, it shows the old value. And because old value was already correct, it doesn't raise any issue. So I added else statement, just to throw an error in a hope that now at least it will show my custom error. But it does not!
Therefore, I have come here for help. Any help is greatly appreciated.
TL;DR Why custom error message doesn't show up for one field. But for other fields it works perfectly fine.
Upvotes: 0
Views: 2511
Reputation: 15214
Why the custom error doesn't show up for some fields but for others it works perfectly fine
Because there are two different components are involved:
You've properly configured message source for bean validation and it works.
But then you're customizing typeMismatch
message and looks like Spring MVC doesn't see this. Typically it's enough to define bean with name messageSource
but for some strange reason in your case you need something else.
My guess is that you have 2 contexts and messageSource
is defined in the context that isn't visible to Spring MVC.
UPDATE:
I looked into example project on GitHub (http://github.com/farazdurrani/sscce) and I see that the problem with non-working typeMismatch
message not because of the configuration. It happens because you're not using standard tags and try to show error messages manually (by invoking getDefaultMessage()
method on each error from Errors.getAllErrors()
). If you try to use <form:errors path="*"/>
, you'll see that all error messages have been properly localized.
Upvotes: 1