Is there a way to use more than 1 validator with Spring 3 annotated controller?
I have a Spring 2.x controller that extends SimpleFormController
that is deprecated in Spring 3 in favor of annotated controllers. So I am trying to convert it to use @Controller
, with @InitBinder
and @Valid
for form validation. However, I can't seem to find a way to use multiple validators with the Spring 3.x controller. How to do it?
This is what my controller bean def looks like:
<bean name="/s/account" class="mywork.AccountSettingsController"
p:formView="forms/account"
p:successView="redirect:/app/s/account"
p:commandName="accountSettingsForm">
<property name="validators">
<list>
<ref bean="emailFormatValidator" />
<ref bean="uniqueEmailValidator" />
<ref bean="changeEmailValidator" />
<ref bean="passwordWithConfirmationValidator" />
<ref bean="changePasswordValidator" />
</list>
</property>
</bean>
This is the controller for the page that allows the user to change their email and password. The beans validator is legacy code, but I assume they have been split into separate classes for better reuse.
I'm trying to move the whole thing into a controller class using annotations:
@Controller
@Secured({BaseController.ROLE_LOGGED_IN})
@RequestMapping("/s/account")
public class AccountSettingsController extends BaseController {
private static final String FORM_URL = "/forms/account";
private static final String FORM_NAME = "accountSettingsForm";
@InitBinder(FORM_NAME)
public void initBinder(WebDataBinder binder) {
// TODO: how to inject > 1 validator for the form?
binder.setValidator(...);
}
@RequestMapping(method = RequestMethod.GET)
public ModelAndView get() {
ChangePasswordOrEmailForm form = new ChangePasswordOrEmailForm();
...
return new ModelAndView(FORM_URL, FORM_NAME, form);
}
...
}
As far as I can tell, Spring 3 assumes a 1 to 1 relationship between: Controller-Form-WebDataBinder-Validator. I could create a composite validator that concatenates 5 separate beans validators and delegates calls to them Validator#supports()
and Validator#validate()
, but is this really the best solution?
source to share
This was an old problem.
Adding a comment here:
After spring 3.2.1.RELEASE, DataBinder # addValidators (Validator ... validators) is available .
source to share
Another one I thought was supposed to have a ValidatorFacade which in turn calls all the other validators one by one, this way you don't need to type, rather attach the ValidatorFacade with initBinder and @Valid before your form bean automatically call the ValidatorFacade function and everything that will be done automatically. Just a thought.
source to share
The best solution I can think of is to inject a collection of validators and manually loop through them yourself. So for now I removed the initBinder()
controller from my class, and this is what I added:
private List<Validator> accountSettingsValidators;
// Maps & Collections can't be @Autowired (by type OR name), so use the JSR 250 @Resource annotation to autowire by name
@Resource
public void setAccountSettingsValidators(List<Validator> accountSettingsValidators) {
this.accountSettingsValidators = accountSettingsValidators;
}
@RequestMapping(method = RequestMethod.POST)
protected ModelAndView processSubmit(HttpServletRequest request,
@ModelAttribute(FORM_NAME) ChangePasswordOrEmailForm form,
BindingResult bindingResult) {
for (Validator validator : this.accountSettingsValidators) {
ValidationUtils.invokeValidator(validator, form, bindingResult);
}
if (bindingResult.hasErrors()) {
return new ModelAndView(FORM_URL, FORM_NAME, form);
}
// process validated form
}
In my formControllers.xml
Spring configuration, I create a list of validators for input:
<util:list id="accountSettingsValidators">
<ref bean="emailFormatValidator" />
<ref bean="uniqueEmailValidator" />
<ref bean="changeEmailValidator" />
<ref bean="passwordWithConfirmationValidator" />
<ref bean="changePasswordValidator" />
</util:list>
source to share