Sabooji
Sabooji

Reputation: 53

Spring MVC binding extra objects

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

Answers (1)

Bogdan
Bogdan

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

Related Questions