1

I have a controller with a method that I want to bind multiple command objects too. When I call the method via GET, it works great and both objects get bound. The issue is if I call the method via POST, only the first command object gets bound and the second one is completely ignored.

Simple example:

def register(MembershipCommand cmd1, RegisterCommand cmd2) {
        println(cmd1.email);
        println(cmd2.pass);
        respond([:]);
}

If I call /register?email=test&pass=test then cmd1 and cmd2 get populated

If I call /register with POST data {email:test,pass:test}, cmd1 gets populated and cmd2.pass is null.

Is there a way to make this data binding work using POST? I can't really use GET because file uploads are involved and plus the forms are fairly large.

I know that another option would be to just split the method into 2, 1 for each object and have my form submit to each separately but I want to avoid that if I can.

Any ideas?

3 Answers 3

0

I have created a minimal working project to test your idea. It works like a charm. Below is the snippets.

RegisterCmd.groovy

class RegisterCmd {
    String email
}

edit.gsp

<g:form resource="${this.player}" method="POST" action="customisedUpdate">
        <g:hiddenField name="version" value="${this.player?.version}" />
        <fieldset class="form">
            <f:field bean="player" property="name" />
            <f:field bean="player" property="game" />
            <f:field bean="player" property="region" />
            <label>Email</label><g:field type="text" name="email"/>
        </fieldset>
        <fieldset class="buttons">
            <input class="save" type="submit" value="${message(code: 'default.button.update.label', default: 'Update')}" />
        </fieldset>
    </g:form>

PlayerController.groovy

    @Transactional
    def customisedUpdate(Player player, RegisterCmd registerCmd) {
        println "Calling save ${player.dump()}"
        println "RegisterCmd: ${registerCmd.dump()}"
        //end::save[]
        //tag::save-handleErrors[]
        if (player == null) {
            render status: HttpStatus.NOT_FOUND
            return
        }

        if (player.hasErrors()) {
            respond player.errors, view: 'create'
            return
        }
        //end::save-handleErrors[]

        player.save flush: true

        request.withFormat {
            form multipartForm { redirect player }
            '*' { respond player, status: HttpStatus.CREATED }
        }
        //tag::save[]
    }

The output looks like:

Calling save <com.itersdesktop.javatechs.grails.Player@1c25113d name=Alexis Barnett game=Pandemic region=EAST wins=96 losses=30 id=1 version=4 org_grails_datastore_mapping_dirty_checking_DirtyCheckable_
_$changedProperties=[name:HUE THI MY NGO] org_grails_datastore_gorm_GormValidateable__errors=org.grails.datastore.mapping.validation.ValidationErrors: 0 errors org_grails_datastore_gorm_GormValidateable
__skipValidate=false>
RegisterCmd: <com.itersdesktop.javatechs.grails.RegisterCmd@7ee6bb8c [email protected] grails_validation_Validateable__beforeValidateHelper=org.grails.datastore.gorm.support.BeforeValidateH
elper@3409999e grails_validation_Validateable__errors=grails.validation.ValidationErrors: 0 errors>

If you are interested in the project, please consult it at https://bitbucket.org/itersdesktop/command-objects/src/master/

Sign up to request clarification or add additional context in comments.

2 Comments

What does the request body look like when you submit? I have a feeling that the g:form setup might be whats assisting with binding everything together since that is part of Grails. For my scenario I am using REST services so my post data was bare minimal {email:test,pass:test}
I am pretty sure you can bind whatever you want if you apply exactly how command object or DTO was designed. I have done a lot of data binding in this way in my main project using AJAX call to hit grails controller. The key point here is to set up your command objects to be sent to the server side.
0

The solution to get POST working was to restructure my form data into an object style format.

So instead of {email:test,pass:test}

I would have {cmd1:{email:test}, cmd2:{pass:test}}

1 Comment

This doesn't work for me (I am using grails 3.3.12).
0

For Grails 3.3.X having multiple command objects doesn't seem to be supported.

Comments

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.