Thursday, May 5, 2011

Spring MVC, one controller for "add" and "update" when using setDisallowedFields?

So I've got a simple form through which I can either "add" a new Thing, or "update" an existing Thing.

What I would like is to have one controller that can process both adding and updating. On the outset this seems simple enough, until I consider the problem of using setDisallowedFields in my InitBinder so that the "id" field is not allowed when adding a new Thing.

Currently I have two controllers with what could be identical code except for the InitBinder method.

Any suggestions or advice? (I'm open to the argument that I should want to maintain two controllers also, if you can give me good reasons)

From stackoverflow
  • The method signature to initBinder takes in the HttpServletRequest:

    protected void initBinder(HttpServletRequest request, 
        ServletRequestDataBinder binder)
    

    So perhaps you can have initBinder() check the request parameters to determine if you should conditionally set setDisallowedFields?

    (If this doesn't help, perhaps I'm not understanding the problem correctly...)

  • The way I do this with a single controller is to have a boolean on my command object indicating if it is a new object or not. In onSubmit I can then check the boolean to see whether I need to perform an add or update action.

  • Actually, you should disallow the "id" field both when adding AND updating. Otherwise a malicious user could tamper with the value of the "id" request parameter of an update request and thereby update a different record to the one shown by the form (assuming there's no ACLs or other domain-level security).

    However if you simply disallow the "id" field, the controller will treat the ID as being null, which will work when inserting but not when updating (e.g. it might try to insert a new record instead of updating, depending on what persistence mechanism you're using). So you want the controller to remember the non-editable values of your domain object (not just IDs, but all disallowed fields) between requests so that it can send all the correct values to the service layer or other business logic. This is done using the type-level @SessionAttributes annotation, as follows (other annotations omitted for clarity):

    @SessionAttributes("thing") // the name of your domain object in the model
    public class ThingController {
    
        public void setDisallowedFields(WebDataBinder binder) {
            binder.setDisallowedFields("id", "someOtherUneditableField");
        }
    
        // request handling methods go here as before
    }
    

    For even better security, set the allowed fields rather than the disallowed fields. Either way you need the @SessionAttributes annotation to fill in whatever existing field values are ignored from the request.

0 comments:

Post a Comment