Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues posting nested resource in Grails

I'm having issues understanding how Grails Restful controllers work. I'm attempting to make a post request (see below) to a nested resource. I'm not sure I understand what I need to change to make this work, as it seems that GET requests build the Bid's association with it's parent resource Item, but when I attempt to POST I am warned that the Item cannot be blank.

Any help is appreciated!

Item.groovy

class Item {
    static hasMany = [bids:Bid]
}

Bid.groovy

class Bid {
    Integer ownerId
    Double amount

    static belongsTo = [item:Item]

    static constraints = {
        ownerId nullable: false
        amount nullable: false
    }
}

BidController.groovy

class BidController extends RestfulController<Bid> {
    static responseFormats = ['json', 'xml']
    BidController() {
        super(Bid)
    }
    @Override
    def getObjectToBind() {
        request.parameterMap.put('itemId', params.itemId)
        return request
    }
}

ItemController.groovy

class ItemController extends RestfulController<Item> {
    static responseFormats = ['json', 'xml']
    ItemController() {
        super(Item)
    }
}

UrlMappings.groovy

class UrlMappings {

    static mappings = {
        "/items"(resources:"item") {
            "/bids"(resources: "bid")
        }
    }
}

URL Mappings

Controller: item
 |   GET    | /items                                                    | Action: index            
 |   GET    | /items/create                                             | Action: create           
 |   POST   | /items                                                    | Action: save             
 |   GET    | /items/${id}                                              | Action: show             
 |   GET    | /items/${id}/edit                                         | Action: edit             
 |   PUT    | /items/${id}                                              | Action: update           
 |  PATCH   | /items/${id}                                              | Action: patch            
 |  DELETE  | /items/${id}                                              | Action: delete    
Controller: bid
 |   GET    | /items/${itemId}/bids                                     | Action: index            
 |   GET    | /items/${itemId}/bids/create                              | Action: create           
 |   POST   | /items/${itemId}/bids                                     | Action: save             
 |   GET    | /items/${itemId}/bids/${id}                               | Action: show             
 |   GET    | /items/${itemId}/bids/${id}/edit                          | Action: edit             
 |   PUT    | /items/${itemId}/bids/${id}                               | Action: update           
 |  PATCH   | /items/${itemId}/bids/${id}                               | Action: patch            
 |  DELETE  | /items/${itemId}/bids/${id}                               | Action: delete                    

Post Request

POST /AuctionService/items/1/bids HTTP/1.1
Content-Type: application/json
Host: localhost:8080
Connection: close
Content-Length: 34

{
    "ownerId": 1,
    "amount": 3.00
}

Response

HTTP/1.1 422 Unprocessable Entity
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 25 Jul 2014 17:44:03 GMT
Connection: close

{"errors":[{"object":"auctionservice.Bid","field":"item","rejected-value":null,"message":"Property [item] of class [class auctionservice.Bid] cannot be null"}]}
like image 365
tylerkern Avatar asked Jul 25 '14 18:07

tylerkern


1 Answers

I think you can accomplish what you want by overriding the createResource() method.

@Override
protected Bid createResource() {

    Bid bid=super.createResource();
    bid.item=Item.get(params.itemId)
    return bid;
}

The other default controller actions will probably not work as expected when using nested URLs. You may also want to override queryForResource and index if you want to ensure that you only return Bids that belong to the item in the URL

@Override
protected Stay queryForResource(Serializable id) {
    def itemId=params.itemId

    Bid.where {
        id==id &&  item.id == itemId
    }.find()

}

def index(Integer max) {
    params.max = Math.min(max ?: 10, 100)
    def itemId=params.itemId
    respond Bid.where {
        item.id==itemId
    }.list(params)
}
like image 131
burns Avatar answered Oct 14 '22 21:10

burns