Sunday, 10 October 2004
Why POST is Special
In a recent post, Don gave his take on the enlightening nature of WS-Transfer;
Honestly, WS-Transfer has been in the oven for quite a while. It’s been interesting to see people’s reaction to it.
Stage 1. What good is this?
Stage 2. Ahh, the idea of uniform application protocols seems pretty useful.
Stage 3. Wow, I can model my entire universe using less verbs/operations than I have fingers on one hand. Why would I ever author another WSDL portType?
Stage 4. Ahh, the idea of uniform application protocols seems pretty useful. Specific protocols seem useful too, especially when I find myself overloading PUT to mean “reboot the machine and submit an audit record to my management log.”
I can’t wait to watch the folks outside of building 42 go through the same four stages…
This is interesting. he’s basically saying that the majority of the world’s problems can be solved by a handful of methods (which people might call “REST” or “CRUD”), and that service-specific protocol semantics are only necessary in exceptional cases. In HTTP, most of those cases fit into POST (I think his fingers slipped when he typed PUT).
In this, Don’s showing that he’s not that far removed from the RESTifarian crowd. Considering who he is, this isn’t a small admission. It does beg the question, however, of what the difference between “specific protocols” and POST are; after all, I can already reboot my router, transfer money and do lots of other scary, real-world things with POST.
The More Things Change…
Let’s take Don’s example. Here’s what it might look like as an HTTP request;
POST /power_switch HTTP/1.1 Host: dons-machine Content-Type: application/mmf+xml Content-Length: … <mmf xmlns=”http://www.gotdotnet.com/team/dbox/mmf/1.0”\> <instruction type=”reboot”/> <instruction type=”audit”> <audit-target>don’s mgmt log</audit-target> </instruction> </mmf>
(if you can’t tell, I’m making this up as I go along, for purposes of illustration. Don’t use this language to control spacecraft, nuclear power plants or other mission-critical applications.)
If we ignore the syntactic details and the protocol-specific goo for getting it across the wire (like “HTTP/1.1” and “Content-Length”), the interesting information here can be split roughly into a triple of method, URI, content:
(“POST”, “dons-machine/power_switch”, appdata)
which, using a good HTTP client API, might look like this:
web = http.Dict() MachineStatus = web.POST(‘dons-machine/power_switch’, appdata)
Don, OTOH, would like* a specific method for something so important, perhaps something that would boil down to:
web_service = ws.service('dons-machine') MachineStatus = web_service.RebootAndAuditMe(appdata)
From the standpoint of interface semantics, the difference here is really just one between saying
POST machineMgmtFormat and
MANAGEMACHINE. In the uniform approach, the service-specific semantics are pushed down into the type (media type) and content (entity body) of the data. In the specific approach, they’re surfaced in the interface itself.
This isn’t a big difference. In fact, I’d wager we could easily redesign HTTP to get rid of POST — e.g., declare that those methods whose names are lowercase are resource-specific, while GET, PUT, DELETE and the rest of the uniform crowd would remain uppercase — and not lose anything. Intermediaries couldn’t take advantage of resource-specific semantics in either case, but they’d be functionally equivalent. What’s the big deal, then?
Protocol Engineering is Social Engineering
While there isn’t much technical difference between these approaches, there’s a big gap in how people can use them.
In HTTP, creating new methods is expensive, while creating new URIs is very, very cheap. OTOH, Web services makes creating new methods cheap, while making the creation of new URIs expensive. This is not because either approach is technically limited; it’s due to the design of both the specifications — like WSDL — and the toolkits that people use.
Therefore, a uniform solution is likely to have a large number of resources, with a few methods each, while a more specific one is likely to have a small number of resources (if not one) with a larger number of methods.
If uniform interfaces encourage people to overload methods, the corollary is that specific interfaces encourages people to overload resources; instead of spreading your application out to get the maximum benefit of those uniform semantics (e.g., GETting the current state of your machine’s power switch), you tend to pile everything onto one poor resource.
For example, take a look at WS-MetadataExchange. MEX is the first spec to use WS-Transfer, and it can’t help but define a GetMetadata method to go along with Get, instead of splitting things up into separate resources.
People may be willing and able to make the right choice as to when to have uniform and specific interfaces. Maybe I’m being a bit cynical, but people also tend to go POSTal pretty quickly, and I’d wager that opening up the field won’t help; most people will skip Don’s step three straight to step four, and only then go back and sprinkle a few uniform semantics onto their applications. Unfortunately for them, it’ll be too late; they’ll be locked into a design that has a paucity of endpoints and not enough meat to hang those uniform semantics onto.
- I’m putting lots of words into Don’s mouth in this entry; I trust he’ll correct me where I’m wrong :)