views:

47

answers:

2

Part of our RESTful API will allow users to register an item with a serial number. As the serial number is not globally unique it cannot be used as the identifier of the resource, so we'll use a POST to the parent resource which will generate an identifier, e.g.

POST /my/items

<item serial-number="ABCDEF" />

In the case where the item is not already registered, the HTTP semantics are well defined. We return a Location header, and the registered item as the entity body, e.g.

HTTP 201 Created
Location: /my/items/1234    

<item id="1234" serial-number="ABCDEF" />

However, in the case where the item is already registered, the API should be idempotent and return the previously registered item without creating a new one. My best guess is that it should then return a 200 OK status code, and use the Content-Location header to indicate where the item actually came from, e.g.

HTTP 200 OK
Content-Location: /my/items/1234    

<item id="1234" serial-number="ABCDEF" />

Would this seem reasonable? I'm not entirely clear whether the Location or Content-Location is more suitable in the second case.

+1  A: 

Here is an interesting discussion on usages of the two headers. It claims Content-Location is not defined for PUT or POST so Location is possibly the better option in your case. It is certainly not clear cut which is better though.

Overall I think your approach makes sense though.

Darrel Miller
That article is missing a bit when it says Content-Location isn't defined for PUT and POST, the HTTP spec actually says it isn't defined for the requests but doesn't say anything about the responses. I think that it might be right though in saying that Content-Location is for alternate forms, whereas Location is the actual location. Difficult to be sure from the spec :-S
Greg Beech
+1  A: 

I had similar requirement recently. For idempotent operations PUT is the best way. You're right there is a mismatch between the external-id and the internal one. I solved it by creating a dedicated resource for the external id target:


PUT /api-user/{username}/items/{serialNumber}

Internally I resolve it as a CREATE in case I have no item for 'username' and 'ABCDEF' serial number or UPDATE in case I do.

In case it was a CREATE I return 201 for an UPDATE 200. Further more the returned payload contains both homegrown id and the external serial number like you suggested in your payload.

manuel aldana
Hmmm yes actually I like this one. Although the serial numbers may not be globally unique, there is an almost 100% probability that they would be unique within the scope of a user so it could act as a scoped unique identifier. It would also solve my problem of how a device can check whether it is already registered by using a GET on this URL and checking whether the response is a 200 or 404. Thanks!
Greg Beech