REST - put IDs in body or not?

Let's say I want to have a RESTful resource for people, where the client is able to assign ID.

A person looks like this: {"id": <UUID>, "name": "Jimmy"}

Now, how should the client save (or "PUT") it?

  1. PUT /person/UUID {"id": <UUID>, "name": "Jimmy"} - now we have this nasty duplication that we have to verify all the time: Does the ID in body match the one in path?
  2. Asymmetric representation:
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
  3. No IDs in body - ID only in location:
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID returns {"name": "Jimmy"}
  4. No kind of POST seems like a good idea since the ID is generated by the client.

What are the common patterns and ways to solve it? IDs only in location seems like the most dogmatically correct way, but it also makes the practical implementation harder.


Solution 1:

There is nothing wrong in having different read/write models: the client can write one resource representation where after the server can return another representation with added/calculated elements in it (or even a completely different representation - there is nothing in any spec against that, the only requirement is that PUT should create or replace the resource).

So I would go for the asymmetric solution in (2) and avoid the "nasty duplication check" on the server side when writing:

PUT /person/UUID {"name": "Jimmy"}

GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

Solution 2:

If it is a public API you should be conservative when you reply, but accept liberally.

By that I mean, you should support both 1 and 2. I agree that 3 doesn't make sense.

The way to support both 1 and 2 is to get the id from the url if none is supplied in the request body, and if it is in the request body, then validate that it matches the id in the url. If the two do not match, then return a 400 Bad Request response.

When returning a person resource be conservative and always include the id in the json, even though it is optional in the put.