Reputation: 53
I'm getting some weird binding issue with Spring MVC 3.
My Controller request mapping looks like this:
@RequestMapping
public String save(HttpServletRequest req,
@ModelAttribute("userEditForm") UserEditForm form,
BindingResult formBindingResult,
ModelMap model,
@ModelAttribute("session") AdminSession session) {
// some validation etc
}
The UserEditForm
:
public class UserEditForm {
private User user;
public User getUser() { ... }
public void setUser(User user) { ... }
}
The AdminSession
:
public class AdminSession {
private User user;
public User getUser() { ... }
public void setUser() { ...}
}
What's happening is that when I submit my form, Spring is binding the User
as I expect in my UserEditForm
object, however, the AdminSession
is also having it's User
bound by Spring, in so far as it's property values are also updated.
I'm going to assume it's due to having a user
property in both @ModelAttribute
objects.
I thought that having the BindingResult
after the UserEditForm
form in the method signature would stop this? The objects are separate instances, and my form elements reference the UserEditForm
object:
<@spring.bind "userEditForm.user.name" />
<input name="${spring.status.expression}" />
I've noticed that in the generated HTML it's outputting:
<input name="user.name" />
Hardcoding the name as userEditForm.user.name
gives me errors, so that's not the way forward.
Is there anyway to stop this from happening?
Upvotes: 1
Views: 1620
Reputation: 24590
That's the default behavior when you annotate a handler method parameter with the @ModelAttribute
. Spring takes the request properties and matches them to properties of the objects annotated with @ModelAttribute
. That's what Spring looks at when deciding what to do: your annotations.
Since both UserEditForm
and AdminSession
are annotated with @ModelAttribute
and both have a User
property, a request property named user.name
will get bound to both User
properties.
You tried to include the command name in the input name and got an error. That's because when binding occurs it occurs on your command object and Spring looks for properties on it (the bindinf path is relative to the command object) and off course the expression does not find any property with that name. If you want to use a full name you could wrap the form in another object and use that for your command instead, something like this:
public class UserEditFormWrapper {
private UserEditForm form;
public UserEditForm getForm() {
return form;
}
public void setForm(UserEditForm form) {
this.form = form;
}
}
Now you can use an expression like this in your inputs: form.user.name
and when you submit to your handler method that now looks like this:
@RequestMapping
public String save(HttpServletRequest req,
@ModelAttribute("userEditForm") UserEditFormWrapper formWrapper,
BindingResult formBindingResult,
ModelMap model,
@ModelAttribute("session") AdminSession session) {
UserEditForm form = formWrapper.getForm();
// some validation etc
}
the binding won't be triggered since AdminSession
does not have a form
property.
That's one way to solve this but it's kind of a hack. You don't want to have the request parameters bound to AdminSession
but that's part of your model so you must have created it somewhere and placed it on the model, right? If so, then remove it from the method's parameters and just get it from the model, something like:
@RequestMapping(value = "/test", method = { RequestMethod.POST })
public String handlePost(HttpServletRequest req,
@ModelAttribute("userEditForm") UserEditForm form,
BindingResult formBindingResult, ModelMap model) {
AdminSession session = (AdminSession) model.get("session");
// some validation etc
}
Upvotes: 1