tags:

views:

61

answers:

4

I'm building a collection of RESTful resources that work like the following: (I'll use "people" as an example):

    GET /people/{key}
      - returns a person object (JSON)
    GET /people?first_name=Bob
      - returns a list of person objects who's "first_name" is "Bob" (JSON)
    PUT /people/{key}
      - expects a person object in the payload (JSON), updates the person in the 
        datastore with the {key} found in the URL parameter to match the payload.
        If it is a new object, the client specifies the key of the new object.

I feel pretty comfortable with the design so far (although any input/criticism is welcome).

I'd also like to be able to PUT a list of people, however I'm not confident in the RESTfulness of my design. This is what I have in mind:

    PUT /people
      - expects a list of objects in JSON form with keys included in the object
        ("key":"32948").  Updates all of the corresponding objects in the datastore.

This operation will be idempotent, so I'd like to use "PUT". However its breaking a rule because a GET request to this same resource will not return the equivalent of what the client just PUT, but would rather return all "people" objects (since there would be no filters on the query). I suspect there are also a few other rules that might be being broken here.

Someone mentioned the use of a "PATCH" request in an earlier question that I had: http://stackoverflow.com/questions/3129185/rest-resource-with-a-list-property

"PATCH" sounds fantastic, but I don't want to use it because its not in wide use yet and is not compatible with a lot of programs and APIs yet.

I'd prefer not to use POST because POST implies that the request is not idempotent.

Does anyone have any comments / suggestions?

Follow-up:::

While I hesitated to use POST because it seems to be the least-common-denominator, catch-all for RESTful operations and more can be said about this operation (specifically that it is idempotent), PUT cannot be used because its requirements are too narrow. Specifically: the resource is not being completely re-written and the equivalent resource is not being sent back from a GET request to the same resource. Using a PUT with properties outside of it's specifications can cause problems when applications, api's, and/or programmers attempt to work with the resource and are met with unexpected behavior from the resource.

In addition to the accepted answer, Darrel Miller had an excellent suggestion if the operation absolutely had to be a PUT and that was to append a UUID onto the end of the resource path so an equivalent GET request will return the equivalent resource.

+1  A: 

I really don't see any easy way you could use PUT to create an arbitrary set of people. Unless, you are prepared to have the client generate a GUID and do something like,

PUT /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}

On the server side you could take the people from the list and add them to the /People resource.

A slight variation to this approach would be to get the server to include a link such as

<link rel="AddList" href="/PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}"/>

in the People resource. The client would need to know that it needs to PUT a list of people to the AddList link. The server would need to make sure that each time it renders the /People resource it creates a new url for the AddList link.

Darrel Miller
Thats a really interesting idea.
DutrowLLC
+1  A: 

Using PUT is definitely the wrong verb in this case. POST is meant to do exactly what you are asking. From the HTTP specification:

The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource...

As such, if you want to update multiple resources in a single call, you have to use POST.

Just be cause PUT is required to be idempotent and POST is not, does not mean that POST cannot be idempotent. Your choice of HTTP verb should not be based on that, but based on the relationship of the requested resource and the resource acted upon. If your application is directly handling the resource requested, use PUT. If it is acting on some other resource (or resources, as in your case), use POST.

AdmiralNemo
+2  A: 

POST indicates a generic action other than GET, PUT, and DELETE (the generic hashtable actions). Because the generic hashtable actions are inappropriate, use POST. The semantics of POST are determined by the resource to which an entity is POSTed. This is unlike the semantics of the generic hashtable methods, which are well-known.

POST /people/add-many HTTP/1.1
Host: example.com
Content-Type: application/json

[
  { "name": "Bob" },
  { "name": "Robert" }
]
Justice
Thanks for your response. I understood why PUT wasn't technically correct, but this explains why I should not break this particular rule.
DutrowLLC
A: 

Regarding Darren Miller's suggestion of using PUT to a GUID (I can't comment...), the point of using PUT would be to achieve idempotency for the operation. The litmus test if idempotency would be this conversation between the client and server:

  1. PUT /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}
  2. 204 NO CONTENT (indicates that it all went well)
  3. client loses connection and doesn't see the 204
  4. client automatically retries
  5. PUT /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}

How would the server differentiate the two? If the GUID is "used up" so to speak then the server would have to respond 404 or 410. This introduces a tiny bit of conversational state on the server to remember all the GUIDs that have been used.

Two clients would often see the same I guess, because of caching or simply keeping stale responses around.

I think a smart solution is to use POST to create an (initially empty, short lived) holding area for a resource to which you can PUT, i.e. clients need to POST to create the GUID resource instead of discovering it via a link:

  1. POST /PeopleList/CreateHoldingArea
  2. 201 CREATED and Location: /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}
  3. PUT /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}

This would mean that the lost idempotency would not result in much overhead; clients simply create new GUIDs (by POSTing) if they didn't see the initial 201 CREATED response. The "tiny bit of conversational state" would now only be the created but not yet used holding areas.

The ideal solution would of course not require any conversational state on the server, but it eludes me.

mogsie