Using HTTP Verbs correctly in your REST Web API
micheltriana.com
micheltriana.com
What I'm struggling with is that REST does not seem to fit with how we use the web today. These HTTP verbs were designed for a document centric era of the web, not for the complex processes we're modeling today. I'm not really seeing REST being easier to design or consume than RPC style web api's--isn't that the whole point?
I mean if you were designing an API to be consumed internally in Ruby/JAVA/C# etc, would you design it like REST? I wouldn't. Why have we all become convinced we need to do this for a web api?
The W3C FORM element is specified with only 'get' and 'post' as permissible values for 'method', which I can only regard as a screwup on W3C's part, but I know of no similar restriction on the methods available to an XMLHttpRequest object; indeed at least one extremely well-regarded Stack Overflow answer [1] confirms that PUT and DELETE work as expected as of five years ago.
The real problem with trying to conform to REST is that there's no rigorous definition, and this leads to the same verb being used for slightly different purposes among APIs.
POST is not - so possibly it's considered safer to use among muggles.
I also think PUT and DELETE are not cacheable, while POST is (given the right set of headers).
caveat: I might be completely wrong about my interpretation.
Strictly speaking, why would a POST return something other than status info and a URL for the created resource (if successful)?
The spec says:
If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header (see section 14.30).
Is it assumed that the client will automatically follow that Location header and GET this resource? That would make some sense (perhaps most of the time) but that's different from having the POST request itself return the resource directly which is what's suggested by this chart.
But if we take that route at what point do we start telling people they're doing it wrong or that they're API isn't _really_ RESTful?
I'm inclined to think the answer is "never" and just encourage people to create APIs that are appropriate for the actual circumstances.
There are generally practical reasons to be RESTy, but "a foolish consistency" and all that comes into play as well.
It's potentially, with a large resource, a lot of wasted bits if all the client wants is status confirmation, but, in a strict request/response setup, it saves you a server roundtrip.
In SPDY / HTTP/2.0 it might make sense to send a smaller request that really is just a status response while pushing the actual resource to a client willing to accept it: in cases where the followup action would be to fetch the request, you won't need an extra roundtrip (though you will get a few extra bits of bandwidth for the intermediate response.)
The status codes are:
/api/users:
GET 200 OK
POST 201 Created (or 205 thanks lgierth)
PUT 204 No Content
DELETE 405 Method Not Allowed
PATCH 204 No Content
/api/users/123:
GET 200 OK
POST 405 Method Not Allowed
PUT 200 OK
DELETE 204 No Content
PATCH 200 OK
return 404 if resource not found, or 410 Gone if you know it once existed
return 400 (or 422) and list of errors on invalid input.
return 401 if need to login.
return 403 if logged in but not allowed to do something.
return 409 Conflict on currency control issue.
I wish there was a BATCH keyword with some 10x/20x codes to enable pipelined HTTP methods. SPDY/HTTP2 should help with some of that.
IMO 422 is a better match, it indicates semantic errors.
Say maybe thus:
GET /users/123 HTTP/1.1
Host: www.example.com
Connection: keep-alive
HTTP/1.1 200 OK
Content-Type: text/json
Content-Length: 41
{"name":"Joe","email":"joe@example.com"}
PUT /users/123 HTTP/1.1
Host: www.example.com
Connection: keep-alive
Range: 24-27
j.random.hacker
HTTP/1.1 200 OK
Content-Type: text/json; charset=ISO-8859-1
Content-Length: 53
{"name":"Joe","email":"j.random.hacker@example.com"}
[1] http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14...The versioning problem can be trivially handled by means of the entity tag mechanism [1]. Our notional RFC need only specify that servers supporting partial PUT requests must include an ETag header [2] in every GET response, and that a client issuing a partial PUT request on a given resource must include an If-Match header [3] with the entity tag it received in its most recent GET response.
The server can then compare the request's If-Match value with the current entity tag for the resource. If they match, the server updates the entity and responds with 204 No Content; otherwise, the server responds with 409 Conflict, whose response body is the complete entity and whose headers include the matching ETag, so that the UA can identify the conflict and present it to the user for resolution in whatever fashion it sees fit.
Your point regarding blind byte-range modification of structured data ("Using json to patch json is more reliable") is not without merit, but belongs at a higher level of abstraction than that at which HTTP operates. If you want to take the JSON document through a thaw-edit-freeze cycle, you can do it on the client and just PUT the result as a complete entity.
[1] http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.1...
[2] http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14...
[3] http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14...
DELETE /client/acme_inc/users
And it's also wrong viewed in light of RFC 2616 (HTTP/1.1), at least with regard to PUT (particularly, it doesn't address the case where the spec indicates the server must use a 301 for a PUT.)
Your remark that "URLs are not encrypted when using HTTPS" does not match the real world.
It is still bad practice since the unencrypted URL can be stored in server logs which could be problematic depending on the application, security requirements, compliance requirements, etc. Unencrypted URLs are also stored in the browser history which would not matter unless the API is being called in the browser.
http://stackoverflow.com/questions/499591/are-https-urls-enc...