Say I want to write an interface exposing the set of books a user has read. Since I want URIs to be human-readable, they'll be of the form /books/War-and-Peace. Adding a book to the set seems like a PUT operation, as it 1) creates a new resource, and 2) is idempotent, since a set cannot have duplicate elements. However, the PUT method requires the client to know the URI of the resource to be created, which must be calculated on the server in this case, since there may be multiple books with the same title, which would be represented as /books/War-and-Peace and /books/War-and-Peace-2. A hacky solution, of course, is to use POST, which is allowed to create new resources, but is not guaranteed to be idempotent.

My solution is the following:

The client PUTs to /book-hashes/<hash>, where <hash> is a hash of the book object.

The server creates a reference to the object at both that URI and /books/<title>.

The server issues a 303 See Other status code pointing to /books/<hash>.

The client GETs /book-hashes/<hash>.

The server responds with a 301 Moved Permanently, pointing to /books/<title>.

The client GETs /books/<title> and stores it as the new URI for the book resource.

The server deletes /book-hashes/<hash>.

Is this a legitimate REST solution? Has anyone solved the problem a different way?

Sure, but I can't guarantee to users of the API that the request is idempotent - that's why PUT is so attractive.
–
Alek StormJun 4 '11 at 6:06

What part needs to be idempotent? You're adding the book to the collection, and you have a mechanism to resolve duplicates (the renaming), what more do you need?
–
Will HartungJun 4 '11 at 6:43

Ah, I wasn't clear - the renaming is for books with the same title but different authors. Adding a book to the set that is completely identical to a pre-existing book has no effect, a property which should be inherent in the API.
–
Alek StormJun 4 '11 at 6:51