Implementing Partial Updates In RESTful Services
[In the book REST In Practice, by Ian Robinson, Jim Webber and Savas Parastatidis] there was one concept I found puzzling. the authors recommend using POST to update the state of a resource. This is driven by a choice of interpretation of the semantics of PUT. According to the HTTP spec:
If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.
In the book, the authors interpret this to mean that the body enclosed with the PUT request should contain the same elements as the representation served by GET requests at the same URI. Since we are using the HATEOAS style, representations include links and other hypermedia controls. The implication is therefore that clients should PUT representations containing both business data (for example the new contents of the coffee order) and hypermedia controls (the available next steps in the workflow).
Alex is of the opinion that the hypermedia and resource representations from the services should drive the workflow of the clients and he suggests four possible ways to model this interaction. Some of these examples harks back to the article on RESTBucks.
[…] it's not widely supported. It also feels semantically wrong to me. From the client's point of view, the business data comprising an order (how many lattes?) is the whole resource. The client doesn't see this as a PATCH, but as a replacement, i.e. a PUT. PATCH might make sense to express concepts like "use skimmed milk in the latte, instead of the full fat I originally ordered". Use POST This is the approach suggested in the book. For me, it has similar drawbacks to PATCH. POST implies appending to a resource. POSTing one cappuccino to a coffee order resource feels like it should add one cappuccino, not replace the existing set of ordered coffees with one cappuccino. Use PUT, including hypermedia To follow the strict interpretation that PUT should include entire representations, the client sends both the entire new coffee order and whatever hypermedia controls the service last sent it. Use PUT, don't include hypermedia The client sends a complete representation of the new order, but no links. To me, this feels conceptually right. The client fulfils the expectations of PUT by sending a complete representation of the parts of the data for which it is responsible, but does not pretend to be responsible for determining what hypermedia controls are available.
In response to GET requests, services serve complete representation of the current known state, including business data and available hypermedia controls. Clients PUT complete representations of the parts for which they are responsible.
William Vambenepe adds additional considerations to this discussion
Let’s pick a simple example. What does it mean if an element is not present in a partial update? Is it an explicit omission, intended to represent the need to remove this element in the representation? Or does it mean “don’t change its current value”. If the latter, then how do I do removal? Do I need partial DELETE like I have partial PUT? Hopefully not, but then I have to have a mechanism to remove elements as part of a PUT. Empty value? That doesn’t necessarily mean the same thing as an absent element. Nil value? And how do I handle this with JSON?
He asserts that the problem of designing the partial resource update interaction is a hard problem and has already been attempted to be solved via specifications such as WSDM, WS-Management and WS-ResourceTransfer.
The good news is that we’ve made a lot of the mistakes already and we’ve learned some lessons (see this technical rant, this post-mortem or this experiment). The bad news is that there are plenty of new mistakes waiting to be made.
Stu Charlton also weighs in on the problem and blames the fact that RESTFul hypermedia cannot really describe a data-model. According to him the two things that RESTful services need for better interoperability are
(a) a [closed] data model that covers 80% of common use cases and can be formatted with JSON and XML.
(b) a media type that covers 80% of common use cases to describe a resource's lifecycle and state transitions -- in other words, to make POSTs self-descriptive in hypermedia. Because the world of computing isn't just about updating data, it's about abstractions.
In the comments to Alex’s post Duncan Cragg weighs in with a practical solution.
You should have /two/ resources: a client order telling the server what /it/ wants, and a server "ticket" that confirms the details back to the client - and adds whatever extra data, links, etc, it wants. The server's ticket has a dependency on that client order, /plus/ on any internal or external process state it's privy to in the execution of the order. But who hosts the client order? Why, the client of course! Well, unless you have an asymmetric set-up, in which case the client can POST and/or PUT its order somewhere on the server. Now the client submits and changes the /whole/ order resource in one go, without worrying about any server-generated bits.
There have been many proposals to solving this problem; and it appears that it is easily solved if we model the resources appropriately. Often times just thinking of resources as entities that support CRUD is the problem and modeling resources as “resources” and the services they offer as in Duncan Craggs example is the solution. The larger problem however is how to get everybody to agree on the solution. Be sure to check out the original posts and the comments and share your opinions on this forum as well.
Lifecycles and State Transitions?
>> Because the world of computing isn't just about updating data, it's about abstractions.
I am not quite sure where Stu got such a radical idea, but in the end the true "decoupling" between two software agents is achieved by expressing a request as an intent and a service as an offer, not just by surfacing the lifecycle and state transitions. It seems that Google is going in that direction with Android. I have talked about this pattern since 2004 (www.ebpml.org/csfsoa.ppt), but again, what do I know?
Re: Lifecycles and State Transitions?
I am not quite sure where Stu got such a radical idea, but in the end the true "decoupling" between two software agents is achieved by expressing a request as an intent and a service as an offer
I completely agree. In this case, what the "service offers" is guided by hypermedia and the clients intent is expressed by the verb. So adding self-descriptive POSTS in hypermedia, if I understand it correctly, is really useful when the client isn't a browser but a software agent like you said.
Re: Lifecycles and State Transitions?
for me the real connection between intent and offers is more on the content negotiation side. Unfortunately, very little work is done in REST in that area. People focus mostly on CRUD operations (including "partial update"), and self-descriptive RPC calls (how does self description works again between two software agents? without a common antology?). They still believe that they will send a bunch of stuff to a client, and it will be enough for the client to "operate". Do they even understand the simple idea that the client itself has states that are unknown to the server. For instance I receive an PO with an Update or Cancel action. Why would the server/service care when the client authorizes the user (or software agent) to move the PO into an editable state? yet, it is a (client-side) action, with authorization rules on the client, totally unknown from the server.
The truth and the matter is that REST got us in the same ditch as WS-*, except that we don't even have a contract to understand what's going on, only Bill who on vacation and Joe who just got laid off, knew exactly what this "service" is supposed to return. Why are we there? because pretty much the same people are at play since the CORBA days. They cannot emmotionally disconnect from RPC and its wonderful application programming model MVC.
How do you get out of there? Simple.
a) there are several types of application programming models. Yes CRUD works for a (small) class of application, MVC, well we should forget about MVC, and sure, there is a large class, a very large class of resources that have a lifecycle. Even our morning coffee order has a lifecycle (duh?). So why not focus on that class since that's pretty much 90% of all information systems?
b) Understand that everytime you will tie an interaction protocol to an OO runtime, you will inevitably fall back to RPC. OO forces you to think unidirectionaly, ignore versioning, forget about events, and marshal everything as a method call. How smart do you need to be to understand that OO is the problem and there is no magic protocol that we will ever wire to an OO based programming model (such as the wonderful MVC) that will ever get you away from the RPC mindset.
c) Hence, build the programming model around the interaction model, not the other way. How many more rounds of middleware will we ever need to understand that very simple idea? Shouldn't Spring, OSGi, SCA... give you an idea that there is indeed a life beyond OO? That the OO model and MVC invented in the 70s are totally antiquated today.
my 2 LOCs
Shane Hastie on Distributed Agile Teams, Product Ownership and the Agile Manifesto Translation Program
Shane Hastie Apr 17, 2015