we need to send all article data back to API for updating and multiuser work could not be implemented. For instance editor could send 5 seconds older data and overwrite fix that some other journalist just did 2 seconds ago and there is no way that I could explain to clients this since those publishing an article is really not in any way connected to updating the content.
This sort of thing is a challenge no matter what you do, It's a very similar problem to distributed source control (mercurial, git, etc.), and the solution, spelled in HTTP/ReST, looks a bit similar.
Supposing you've got two users, Alice and Bob, both working on /articles/lunch
. (for clarity, response is in bold face)
First, alice creates the article.
PUT /articles/lunch HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0
Hey Bob, what do you want for lunch today?
301 Moved Permanently
Location: /articles/lunch/1
The server did not create a resource, because there was no "version" attached to the request, (assuming an identifier of /articles/{id}/{version}
. To perform the creation, Alice was redirected to the url of the article/version she'll be creating. Alice's user agent will then reapply the request at the new address.
PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0
Hey Bob, what do you want for lunch today?
201 Created
And now the article has been created. next, bob looks at the article:
GET /articles/lunch HTTP/1.1
Host: example.com
Authorization: Basic Ym9iOnBhc3N3b3Jk
301 Moved Permanently
Location: /articles/lunch/1
Bob looks there:
GET /articles/lunch/1 HTTP/1.1
Host: example.com
Authorization: Basic Ym9iOnBhc3N3b3Jk
200 Ok
Content-Type: text/plain
Hey Bob, what do you want for lunch today?
He decides to add his own change.
PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic Ym9iOnBhc3N3b3Jk
Hey Bob, what do you want for lunch today?
Does pizza sound good to you, Alice?
301 Moved Permanently
Location: /articles/lunch/2
As with Alice, Bob is redirected to where he will be creating a new version.
PUT /articles/lunch/2 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic Ym9iOnBhc3N3b3Jk
Hey Bob, what do you want for lunch today?
Does pizza sound good to you, Alice?
201 Created
Finally, Alice decides that she would like to add to her own article:
PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0
Hey Bob, what do you want for lunch today?
I was thinking about getting Sushi.
409 Conflict
Location: /articles/lunch/3
Content-Type: text/diff
---/articles/lunch/2
+++/articles/lunch/3
@@ 1,2 1,2 @@
Hey Bob, what do you want for lunch today?
-Does pizza sound good to you, Alice?
+I was thinking about getting Sushi.
Instead of being redirected as normal, a different status code is returned to the client, 409
, which tells Alice that the version she was trying to branch from has already been branched. The new resources was created anyways (as shown by the Location
header), and the differences between the two was included in the response body. Alice now knows that the request she just made needs to be merged some how.
All of this redirection is related to the semantics of PUT
, which requires that new resources be created exactly where the request line asks. this could also save a request cycle using POST
instead, but then the version number would have to be encoded in the request by some other magic, which seemed less obvious to me for the purposes of illustration, but would probably still be preferred in a real API to minimize request/response cycles.
api/article?action=publish
? Query parameters are intended for such cases where the state of the resource is dependent on the 'algorithm' (or action) you mention. E.g.api/articles?sort=asc
is valid