users@jersey.java.net

Re: [Jersey] Jersey's (experimental) approach to support hypermedia constraint

From: Jan Algermissen <algermissen1971_at_mac.com>
Date: Wed, 10 Feb 2010 16:21:09 +0100

On Feb 10, 2010, at 2:37 PM, Santiago Pericas-Geertsen wrote:

>> Putting something like order.pay() in the client code
>> violates RESTs hypermedia constraint because it hard
>> codes an assumption that REST does not support. In REST
>> there is no way to know at design time whether there
>> will be a 'pay' traversal froom the 'order'-state at all.
>
> What REST constraint is that violating? How could a bot be programmed without the basic knowledge of things like pay, ship, etc.?

A client developer needs to know about the hypermedia a service makes use of. If it does not, there is just no way to code anything. The client developer might not know about every hypermedia the service might use (mustIgnore rules assumed) but at least some of it.

The second thing the client developer can rely on is that a service does not lie. If a client comes across <img src="/foo" /> in a text/html representation it can assume that the resource /foo is an image[1]. A server that serves a stylesheet for /foo can be considered broken.

What a client developer must not make specific assumptions about is the representation it receives when sending a GET request to /foo. It simply must do its best with whatever it receives for GET /foo. The server is free to change whatever it sends back at any time.

Thus, when a client sees (and understands of course) something like this

Content-Type: application/orderlist

<orders>
  <order href="/orders/1" />
  <order href="/orders/2" />
  <order href="/orders/3" />
</orders>

it can savely assume that the server is not lying and that /orders/2 is in fact an order[1] but it must not assume anything about the representation it will receive for GET /orders/2. If it would make such assumptions, server and client would be coupled with the effect that the server owner would need to know that he will break this client if it changes the representation it sends for GET /orders/2.

REST deliberately aims to avoid exactly this. As a server owner, I can change the service in any way I want to, as long as I keep the semantics of the resources stable (and ideally make my URIs cool, that is: maintain them 'forever').

If a frameworks hides an assumptions about the GET responses for resources in an OO API it creates the impression that there was any way to keep the server from changing (which is a lie in a networked system).

Providing something like order.pay() implies that in the response to GET /orders/2 will be a link that can be mapped to the pay() action. But we cannot know that at design time. Or we are not being RESTful but doing RPC.

The only RESTful way to approach this is

- GET /orders/2
- dispatch on whatever media type we received (or on available headers)
- if have-link-header 'pay' OR media type == application/order AND have pay-link in the representation THEN (and only then)
  - do a POST on the target of the pay link (we know this from corresponding spec (and maybe form in representation))
  - GOTO handle next state
- if media type == [something else]
  - try out some other rule to persue the goal of 'paying'
- if 4xx
  - handle 4xx
....

The issue is that REST does not allow us to view one particular representation as the 'correct' server behavior and the others as 'failures'. Even 4xx responses must be considered part of the normal conversation between client and server.
It may be hard to accept, but anything else would not be REST. With the result that server owners have to know what their clients are assuming besides hypermedia semantics.

My deepest concern is to get this point across to developement teams (that do not have the time to dive into REST too deeply) and order.pay() simply works against that goal because it makes it look as if there was any guarantee that that link will be available. There is no such guarantee in REST.

And try..catch around order.pay() would not be helpful because it would even more emphazise the idea that of order.pay() failung being an *exception*. It is not in REST.

Phew - hope you can figure out from that what I am trying to say.

Jan

[1] Let's for now not tackle the question what that means.


-----------------------------------
 Jan Algermissen, Consultant
 NORD Software Consulting

 Mail: algermissen_at_acm.org
 Blog: http://www.nordsc.com/blog/
 Work: http://www.nordsc.com/
-----------------------------------