"Just" use POST

Tim Bray: "But maybe Joe needs a bigger club, because I have to admit that limiting myself to GET and POST just doesn’t cause me that much heartburn."

I get asked a lot about PUT v POST, as do other people associated with REST based design. The question comes up online frequently as well (eg it's a regular topic on the rest-discuss and atompub lists). Usually it's in the context of updates via forms posting or how to change just a few data fields. "How do I change the title of an entry?" is a very common and valid use case. Forms posting is easy to code to and highly portable - almost all deployed client and server libraries support (and are often optimised for) forms posting.

The pro-REST answer is to use PUT. PUT means update the resource with this entity, which tends means "overwrite". Let's think for a moment about how that works for things like tags in a blog post - if I leave the tag out, am I saying remove it or ignore it? On the server side, a PUT to a resource involving embedded lists (eg tags in Atom/RSS entries) tends to result in ugly code when either the backing system is an RDBMS or the representation is any "joined" structure in the persistence layer - they'll have to diff what's persisted against what's sent, which for 99% of people means a "select for update" pattern (a double for loop cross-referencing the posted tag list with the database tags is a sure sign you've hit this problem). Yes, you can store the entity straight to disk or use a non-relational architecture - but now you have N indexing problems, something a relational database "just" solves for the 99.9% of developers who don't have a megadata problem.

So PUT often feels wrong or contorted to developers who literally want to mod a couple of fields. Hence PUT is much less popular in the wild than forms posting (all aside from the fact that PUT is excluded from HTML4 forms).  In other words, people tend to see PUT as a heavyweight, sucking, POST. In turn they "just" use POST+forms.

Are we done? Unfortunately, no.

When does PUT v POST actually *matter*? It matters, as far as I can tell, when your resource stands for a collection, which is very common - folders, albums, feeds, collections, blogs, tagclouds, orders, a shopping cart - any list based structure.

Let's take AtomPub as an example - to add something to a collection using AtomPub, you use POST:

POST /collection
host :example.org
content-type: image/png

...binary...



Easy, and you can update that uploaded object later via PUT. Updates to the collections themselves are undefined in AtomPub.  But let's ask, how would we do that? We could PUT the Atom feed (san the contained Entries) back to the collection URI. So imagine we want to change only the title - isn't an entire PUT of an Atom feed (san the contained Entries) verbose, inefficient and stupid for that simple usecase? We could "just" use a form post instead:

POST /collection
host :example.org
content-type: application/x-www-form-urlencoded

&title=foo


Ahh. Boom. Updating the collection in this way uses the same verb as the adding to the collection. How to tell the difference in client intent? The answer here for most people, will be to use the fact that forms posting has a specific media type - so the media type "qualifies" the operation. This definitely isn't REST style, as the verb is no longer uniform; at the same time it's not an abstract concern - there'll be a big switch in the code somewhere that looks for the media type - exactly the kind of thing good programmers hate.  Let's remember that AtomPub servers aren't limited to blog posting - they can accept any media type they declare support for, adn thus can act as generic upload systems (if you have a stable network, more on that another time).

One workaround could be that if the client sent a corresponding "ID", like this:


POST /collection
host :example.org
content-type: application/x-www-form-urlencoded

&title=foo&id=http%3A%2F%2Fexample.org%2Fid%2Fefgfeacbe

the server could detect that the ID is present. It feels funky though, aside from having to map the field/keys in your precious snowflake format into forms parameters

Speaking as a member of the IEFT WG, perhaps we shouldn't have skipped collection updates in AtomPub as it would have made the overall constraint clearer - POST can't be used in the general case for updates to collections, ergo PUT is the only uniform approach to updating their content. On the other hand lots and lots and lots of people don't, won't (and sometimes can't) care about REST/HTTP/AtomPub arcana. So some part of me thinks we need patterns and practices to help developers jfdi.

Fwiw, like Tim, I can live with the forms POST option, to either update a collection or perform a partial write. But think about it for a bit - switch on type is a fairly ugly workaround. Not quite RPC, but problematic. Blog entries in turn are often collections (containing media), as are the folders you find in WebDAV and so on - it's not a problem specific to AtomPub.

So when you ask a pro-REST person about why not "just use forms" for partial updates instead of having to write out the entire data to send to the server via PUT, and they go "uhm, uhm,...", this is the kind of design kludge they're thinking about. Maybe you could PUT a form as a workaround for partials - I think that could work better than POST or having special "edit" URIs for anything collection-like. But as far it goes as I'm not sure we in the pro-REST community have a good general answer or design pattern for partially updating a resource. Until we do, I predict people will tend drop down to using forms posting as it's the easiest and most portable approach for deployed client libraries and web frameworks. That or define some other specialised media type for partial updates.

Tags:

12 Comments


    Very nice post -- thanks.

    In our system, every collection is also available as an entry in a "collections" collection. Updating is simply GET/PUT on the proper entry. I don't see anyway around that in AtomPub, where the "entry" is king and feeds (not collections) are secondary. So everything has an atom:entry representation.

    For partial updates, we settled on a local atom extension: an attribute "x:edit-id" on any element in the entry that is exposed as an editable sub-resource to the entry (AND that has text content -- we happen to use categories w/ text content regularly which some will frown on). The value of x:edit-id is simply the URI for that resource -- handy for AJAX "delete" as well as "put" (mostly "put"-ing text/plain). For regular full-resource AtomPub PUT, we settled on: expose an atom extension link that offers a JSON version of the atom:entry. On form submit, grab the json and diff it against current values in the form, then run it through a little javascript template function we have to serialize out atom+json to atom+xml and let XHR "put" it back to the AtomPub endpoint (the "edit" link).

    This all seems a bit complex, but once the pieces are in place it is quite clean to work with. I am not opposed to form posting w/ x-www-form-urlencoded, but every developer ends up doing it differently and you end up with RPC-ish/hard-to-maintain apps.

    This is all just by way of a plug for Atom/AtomPub -- I do think it is widely applicable and offers a nice set of constraints if one is willing to address a few pain points. And who knows, maybe some of these techniques will get wider play and make browser-based AtomPub easier for everyone.


    I like to think I'm pro-REST, but I would have designed it differently. Ask the collection to tell you how to change it's title, and how to add a new entry, and those would be two different URLs that can both use the same method (or not). With a browser in the front this pattern would feel really natural.

    If it feels funky, I think it has something to do with the combination of CRUD around the resource in addition to REST.


    I don't know Atom, but I agree with Assaf--the resource should tell you how to update it.

    Seems like it should be as simple as:

    PUT /collection/title
    host: example.org
    content-type: text/plain

    foo


    Sascha: "A simple decision for us to use POST or PUT is technology based. In Java ME (MIDP 2.x) PUT is not obligatory. In that case..."

    Thanks, I know the limitations of mobile tech, as well as the limitations of tech like HTML4 (where PUT is not available at all). The point is there aren't patterns to deal with the limitations. Fobbing it off to semantics doesn't work for me, not the least because having to write per client device servers is nutso.

    Mike: "PUT /collection/title"

    As long there's no Entry in that collection, with that URI...

    Assaf: "I like to think I'm pro-REST, but I would have designed it differently. Ask the collection to tell you how to change it's title"

    How do I ask, in a way that is more uniform than using a form?


    Excellent post. Dunno whether any pingback thingy works, but I gave a shot out from:

    http://broadcast.oreilly.com/2009/02/...


    What about HTTP PATCH ?

    https://datatracker.ietf.org/drafts/d...


    Bill: "the media type "qualifies" the operation [...] there'll be a big switch in the code somewhere [...] Remember AtomPub servers aren't limited to blog posting - they can accept any media type they declare support for."

    Isn't media resource posting one of those big switches? so POST in AtomPub doesn't appear to be uniform (for strictest definitions of uniform): it performs two kind of appends.


    I totally agree with everything you've just said Bill - good post. For me the big win of PUT is knowing its idempotent where with POST unless you know exactly about a specific service you've no idea.

    However if you want to build a RESTful service that allows web browsers to integrate with it (which is kinda handy :-) which have to rely on just GET/POST how about a solution to your example being...

    For inserting to the collection...

    POST /collection

    For updating an item in the collection, use a different URI

    POST /collection/someId

    The both lose the idempotent feature of PUT unless you use the POST-returns-redirect-unique-url trick that Atom uses but you can still use that with POST


    More uniform? How do we measure degrees of uniformity?

    As far as REST is concerned, if you have a generic content type that can express hypermedia, you're fine. HTML does that. Atom to some extent (links), and could definitely benefit from actions. In fact, a message that tells you how to update an entry, add a collection, etc by expressing the action is more self-describing than one that sends you look at the specification. I believe self-describing is part of the uniform interface REST talks about.


    POST is also superior to GET as a partial CSRF countermeasure

    http://en.wikipedia.org/wiki/Cross-si...


    Google's AtomPub feeds allow you to set the header X-HTTP-Method-Override, whose value can be PUT or DELETE, in order to break out of sandboxes that don't allow these verbs. So if you POST with the appropriate header, the operation is conceptually a PUT or DELETE as the case may be, and is remapped at a low level.

    Disclaimer: I don't speak for Google, and I'm only restating what's a matter of public record anyhow.


    It's a bit astronautical, but I kinda agree with Assaf. The question of granularity : if the title is significant enough that you might want to change it independently of other pieces of data, then it should probably have its own URI.