1

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?

1
  • If you are going to hard-code the name, I think you have to do a fully-qualified name, starting with the Model Attribute name. So, in your example, I would expect <input name="userEditForm.user.name" or "session.user.name". Spring uses the request parameter names to de-marshal the objects, so the names have to match. Commented Feb 1, 2013 at 18:52

1 Answer 1

1

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
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Bogdan, I wondered if that would be the answer. Nice explanation, all makes sense to me now!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.