GET and POST are RESTful enough

2010-06-22 @ 08:34#

every once in a while i get into a discussion about the REST arch style w/ a person who is under the impression that, unless you use the HTTP methods PUT and DELETE, your app is not RESTful. this is not correct. REST is not concerned w/ which methods you are using. HTTP, however, requires your application use any HTTP methods correctly.

is it safe?

for example, it's important that implementations do not use safe methods (e.g. GET and HEAD) to perform unsafe operations (e.g. writing data). the classic example of using safe methods to peform unsafe operations is:

  GET /users/123?action=delete

since HTTP defines GET as "safe", caches and other proxies will assume (based on the spec) that it's fine to perform "pre-fetch" on these URIs and/or cache and replay the responses as needed. with the URI example given above, it's possible that bots and other intermediaries will blissfully delete content from servers even when that is not the intention. bummer, eh?

happily, there's an easy fix for this problem. don't use safe methods for unsafe operations!

POST is not enough?

another common notion is that using only POST for all write operations is not RESTful. IOW, you can't claim your implementation supports REST unless it utilizes PUT and/or DELETE. this incorrect assumption is often a side-effect of viewing REST only through the lens of CRUD operations; that REST == CRUD over HTTP. again. while it is possible to do CRUD over HTTP, that's not REST; that's CRUD over HTTP. not that there's anything wrong w/ that.

it's perfectly fine to use POST for write operations; specifically update operations for existing resources. yes, PUT has additional semantics defined by HTTP (not REST) that make it very helpful for updates, but (again) that's HTTP, not REST.

please, let's move on

about a year ago, Roy Fielding addressed this mis-understanding about POST for updates on his blog:

I think the anti-POST meme got started because of all the arguments against tunneling other protocols via HTTP?s POST (e.g., SOAP, RSS, IPP, etc.). Somewhere along the line people started equating the REST arguments of ?don?t violate HTTP?s method definitions? and ?always use GET for retrieval because that forces the resource to have a URI? with the paper tiger of ?POST is bad.?

Please, let?s move on. We don?t need to use PUT for every state change in HTTP. REST has never said that we should.

Roy, T. Fielding, Mar-2009 : It is okay to use POST

but can i really do it all w/ just GET and POST?

sure you can; it's been done that way for many years. nothing magical is needed. no special headers; no action arguments in the URI; no method parameter in the body of the message.

one (simple) way to handle write actions for method-restricted environments is to treat these writes as "queued" operations on the server. for example, expose public resources where writing to the server is handled via requests that are added to a list for processing:

  POST /users/pending-updates/
  POST /users/pending-deletes/

this pattern lets your provide clients w/ feedback on the processing of these requests via a GET against that same /pending... resource (or a sub-ordinate resource that represents each change request). this model even supports long-running processes if you return 202 Accepted as the status and direct clients to view the GET-able resource to see progress reports.

implmentations like the one shown above work because HTTP allows the resource model (RM) to be abstracted from the data/object/storage model (DOSM). it's perfectly fine - advisable, in fact - to express a public RM that differs from the server's internal DOSM. so, when the HTTP methods available are restricted (usually due to client limitations), you only need to focus on an appropriate RM to match the implementation environment and not try to jerry-mander the server's internal DOSM based on this partiular restriction.

we'll do it 'live'

even better, you don't need a single RM for all clients of all types. instead, you can use hypermedia controls within the response representations to return the "next steps" appropriate for the particular client; thereby referencing the appropriate RM space for that interaction. for example, browser-based, non-scripted agents limited to HTML 4.x FORM elements that support only GET and POST can be sent representations that point the client to /pending... resources like the ones illustrated earlier. at the same time, full-featured clients (e.g. desktop applications w/ a fully-functional HTTP-client library) can be sent representations that contain hypermedia controls that support PUT and DELETE.

since HTTP abstracts interactions into resources w/ representations addressed via URIs, making runtime adjustments is not only possible, it's designed into the protocol. by combining these levels of abstraction w/ the hypermedia constraint described in Fielding's dissertation you can achieve a very flexible implementation that is not only HTTP-compliant, but also supports key principles of the REST architectural style across multiple environments; even ones restricted to using only GET and POST.