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?

Raymond Holguin
  • 1,050
  • 2
  • 12
  • 35

3 Answers3

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}}

Raymond Holguin
  • 1,050
  • 2
  • 12
  • 35
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=alexis.barnett@gmail.com 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/

Tung
  • 1,579
  • 4
  • 15
  • 32
  • 1
    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}` – Raymond Holguin May 20 '20 at 17:29
  • 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. – Tung May 20 '20 at 18:59
0

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

David Ha
  • 187
  • 1
  • 1
  • 10