The short answer is that if I'd presented it that way, the
"REST-haters" would have an even better reason to roll their eyes: it
would look like I couldn't think of a resource-oriented way to do
transactions and I'd had to fall back to the RPC style.
Let me explain. When I read this question my immediate vision was
of a request that looked like this:
I don't know if this is what Scott had in mind, but let's
run with it. This actually won't work as is: PUT only works when the
client knows the URI of the new resource, and the URI for a
transaction resource is chosen by the server. That's a minor problem
we can fix by sending POST to a "transaction factory" resource:
My first suggestion about transactions (page 231) was: "You can
expose simple transactions as batch operations..." This is a kind of
batch operation (albeit not quite the kind I had in mind), one that affects two resources identified by URI.
The problem with this representation is that it makes the
second HTTP request look a lot like an RPC-style overloaded
POST request. It looks less like an object the client is creating, and
more like a command that /transactions is supposed to
execute.
I wouldn't go so far as to say it is overloaded POST,
because its result is a new addressable resource (the transaction
record). There's a fuzzy overlap here between RESTful and RPC-style, just
as there is with a request like "GET
/rest-endpoint?method=photos.search&tag=penguin". But in the book I don't
design URIs that look like method calls, because it sets a bad example, and for the same reason I don't
design representations that look like commands. If you looked at
that second HTTP request in isolation, you'd probably think "okay, there's no
fundamental difference between RESTful and RPC-style
transactions". When in fact there is a more elegant way to do them
that looks nothing like the RPC style.
Why do I think the way I showed in the book is more elegant?
Mainly because requests on transaction views work exactly the same way
as would non-transaction requests on the underlying resources. If you
could change an account balance at will, you'd PUT to
/accounts/savings/55. You can't, so you create a transaction
and PUT to /transactions/11a5/accounts/savings/55.
Two lesser reasons. First, you don't have to do the whole
transaction at once. You can build it up on the web service as
real-world events happen on your end, and commit when you're ready. It
works the same way as a transaction in a programming language. Second,
a transaction is an addressable resource even while in
progress. Multiple authenticated clients can inspect or
collaborate on a transaction, and the rules for avoiding conflicts are
the same as on the rest of the web (eg. If-Unmodified-Since).
That leaves the question of efficiency. You can package multiple
operations into a single HTTP request for efficiency's sake. But if a
single operation is an HTTP method applied to a URI, that's what you
should package. If the uniform interface is good enough for
one-at-a-time operations, it's good enough for descriptions of batch
operations. You don't need an application-specific vocabulary like the
one I came up with reflexively. A simple, uniform vocabulary will
do; something like this:
(You could probably use an XML document that contains a series of
SOAP envelopes!)
I think POSTing that representation to a transaction factory
satisfies Scott's original criteria: "create a transaction resource that
included both checking & savings account as well as the transfer
amount... defining the resource as XML in the request body". And it
would make a good addendum to the "Transactions" section, once the
reader has seen which HTTP requests underly the transaction. But
presenting it instead of the multi-request version would
probably be confusing, and would certainly give the impression that
there was no RESTful way to make those HTTP requests one at a time.
Thu May 31 2007 17:26 Transactions of the Transaction Society:
Sam and I got a question from reader Scott Davidson about the famous RESTful
transaction design (quoted at length by Jon Udell here,
in case you bought so many copies of the book that you're now
deadlocked trying to decide which one to look up page 231 in). I think
it's worth responding to at length:
I'm perplexed why your transaction example in Chap. 8 didn't simply
create a transaction resource that included both checking & savings
account as well as the transfer amount w/ a PUT (defining the resource
as XML in the request body). Then you could simply call
/transaction/11a5/commit or even just assume that this is a request to
commit the transaction by default and avoid the 2nd call altogether.
Is there a specific reason why it was not done this way? I can already
see the "REST-haters" rolling their eyes to this three request/response
transaction pattern.
PUT [some URI] HTTP/1.1
Content-Type: application/xml
<transaction type="transfer" amount="50.00">
<source href="/accounts/checking/11" />
<destination href="/accounts/savings/55" />
</transaction>
POST /transactions HTTP/1.1
Content-Type: application/xml
<transaction type="transfer" amount="50.00">
<source href="/accounts/checking/11" />
<destination href="/accounts/savings/55" />
</transaction>
<transaction>
<request method="PUT" target="/accounts/checking/11">
<representation>balance=150</representation>
</request>
<request method="PUT" target="/accounts/savings/55">
<representation>balance=250</representation>
</request>
</transaction>