Reputation: 2589
I'm working on spring mvc application, where I should aplly validation based on Spring MVC validator. I first step for that I added annotation for class and setup controller and it works fine. And now I need to implement custom validator for perform complex logic, but i want to use existing annotation and just add additional checking.
My User class:
public class User
{
@NotEmpty
private String name;
@NotEmpty
private String login; // should be unique
}
My validator:
@Component
public class UserValidator implements Validator
{
@Autowired
private UserDAO userDAO;
@Override
public boolean supports(Class<?> clazz)
{
return User.class.equals(clazz) || UsersForm.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors)
{
/*
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "NotEmpty.user");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "login", "NotEmpty.user");
*/
User user = (User) target;
if (userDAO.getUserByLogin(user.getLogin()) != null) {
errors.rejectValue("login", "NonUniq.user");
}
}
}
My controller:
@Controller
public class UserController
{
@Autowired
private UserValidator validator;
@InitBinder
protected void initBinder(final WebDataBinder binder)
{
binder.setValidator(validator);
}
@RequestMapping(value = "/save")
public ModelAndView save(@Valid @ModelAttribute("user") final User user,
BindingResult result) throws Exception
{
if (result.hasErrors())
{
// handle error
} else
{
//save user
}
}
}
So, Is it possible to use custom validator and annotation together? And if yes how?
Upvotes: 33
Views: 35575
Reputation: 4411
I know this is a kind of old question but, for googlers...
you should use addValidators
instead of setValidator
. Like following:
@InitBinder
protected void initBinder(final WebDataBinder binder) {
binder.addValidators(yourCustomValidator, anotherValidatorOfYours);
}
PS: addValidators
accepts multiple parameters (ellipsis)
if you checkout the source of org.springframework.validation.DataBinder
you will see:
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
....
public void setValidator(Validator validator) {
assertValidators(validator);
this.validators.clear();
this.validators.add(validator);
}
public void addValidators(Validator... validators) {
assertValidators(validators);
this.validators.addAll(Arrays.asList(validators));
}
....
}
as you see setValidator
clears existing (default) validator so @Valid
annotation won't work as expected.
Upvotes: 63
Reputation: 148910
If I correctly understand your problem, as soon as you use you custom validator, default validation for @NotEmpty
annotation no longer occurs. That is common when using spring : if you override a functionnality given by default, you have to call it explicitely.
You have to generate a LocalValidatorFactoryBean
and inject it with your message source (if any). Then you inject that basic validator in you custom validator and delegate annotation validation to it.
Using java configuration it could look like :
@Configuration
public class ValidatorConfig {
@Autowired
private MessageSource messageSource;
@Bean
public Validator basicValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource);
return validator;
}
}
Then you modify UserValidator
to use it :
@Component
public class UserValidator implements Validator
{
@Autowired
@Qualifier("basicValidator")
private Validator basicValidator;
@Autowired
private UserDAO userDAO;
// ...
@Override
public void validate(Object target, Errors errors)
{
basicValidator.validate(target, errors);
// eventually stop if any errors
// if (errors.hasErrors()) { return; }
User user = (User) target;
if (userDAO.getUserByLogin(user.getLogin()) != null) {
errors.rejectValue("login", "NonUniq.user");
}
}
}
Upvotes: 9
Reputation: 780
Well for me you have to delete the
@InitBinder
protected void initBinder(final WebDataBinder binder)
{
binder.setValidator(validator);
}
Leave the
@Valid @ModelAttribute("user") final User user,
BindingResult result
And after in the function make
validator.validate(user,result)
This way you will use the validation basic with the @Valid and after you will put make the more complex validation.
Because with the initBinder you are setting the validation with your complex logic and putting a way the basic logic.
Maybe is wrong, i use always the @Valid without any validator.
Upvotes: 7