Reputation: 2154
I have a Spring 2.x controller that extends SimpleFormController
, which is deprecated as of Spring 3 in favor of annotated controllers. So I'm trying to convert it to use @Controller
, with @InitBinder
and @Valid
for form validation. However, I can't find a way to use multiple validators with a Spring 3.x controller. How do I do this?
Here is what my controller's bean def currently 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>
It's the controller for a page which lets the user change their email address and password. The validator beans are legacy code, but I'm guessing they were split into separate classes for better reusability.
I'm trying to move all of that into the controller class itself, 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 aggregates the 5 individual validator beans, and delegates the Validator#supports()
and Validator#validate()
calls to them, but is that really the best solution?
Upvotes: 4
Views: 4426
Reputation: 560
This was an old problem.
Adding a comment here:
After spring 3.2.1.RELEASE, DataBinder#addValidators(Validator... validators) is available.
Upvotes: 1
Reputation: 1866
Another which i thought was to have a ValidatorFacade which in turn calls all the other validators one by one, that way you dont need to inject rather attach the ValidatorFacade with the initBinder and @Valid in front your form bean will automatically call the ValidatorFacade and everything taken care automatically. Just a thought.
Upvotes: 3
Reputation: 2154
The best solution I can think of is to inject a collection of validators, and manually spin through them myself. So for now I removed initBinder()
from my controller class, and here's 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 config, I create the list of validators to inject:
<util:list id="accountSettingsValidators">
<ref bean="emailFormatValidator" />
<ref bean="uniqueEmailValidator" />
<ref bean="changeEmailValidator" />
<ref bean="passwordWithConfirmationValidator" />
<ref bean="changePasswordValidator" />
</util:list>
Upvotes: 0