RE: More Thoughts on Links and Operation Subclasses

On Friday, January 31, 2014 4:50 PM, Ryan J. McDonough wrote:
> On Jan 30, 2014, at 9:20 AM, Mark Baker <distobj@acm.org> wrote:
> 
> > Our last discussion failed, but this discussion on Ryan's excellent
> > points seems to have drilled down to a bite sized point that I'll
> > offer a quick observation on...
> >
> > On Tue, Jan 28, 2014 at 7:45 AM, Markus Lanthaler wrote:
> >> Yeah, DELETE is very specific and it might look as if it doesn't
> >> have any value. There exist, however, APIs that archive resources
> >> that are being deleted. In such a case, I think it does make sense
> >> to qualify a DELETE with an ArchiveOperation or something similar.
> >> Basically the operation tells the client what kind of consequences
> >> it can expect if it invokes an operation.
> >
> > What you describe there is not REST. With REST, the client only knows
> > that a DELETE was performed upon receiving a 200 response, not that a
> > DELETE was performed along with some other stuff that is indicated
> > separately (either in-band in the message, or out-of-band via
> > information received in a prior interaction). *Just* DELETE, as
> > defined in the HTTP spec.

Right. That's the contract. But AFAICT that doesn't mean that it is a
violation of any of REST's constraints to give a client more information
that it can use to choose which of the potential requests it might wanna
make.

Do you disagree?

IMO it doesn't matter whether a media type or a vocabulary a defines that.


> > The "other stuff" is totally permissible in
> > REST, but is an *implementation detail* hidden behind the uniform
> > interface.

Agreed and intermediaries won't see this at all. And they don't have to..
that's one of RESTs main strengths.


> Yes! That's where I was trying to go with this. What Hyrda seems to be
> doing is specializing HTTP methods rather than simply instructing
> clients as to how they should format messages and what HTTP method to
> send that message to the server. It's a subtle but critical difference.
> HTTP methods have no concept of return types or expected status codes
> and adding them to hydra:Operations is effectively changing the
> semantics of HTTP.

As I've already said a number of times, those are only hints. Even
describing "how [clients] should format messages and what HTTP method to
send that message to the server" is only a hint in the end, especially for
POSTs which have very little semantics on their own.


> HTML is one of the best examples of REST/Hypermedia out there today.
> Ignoring the fact that HTML isn't the greatest format for machine-to-
> machine interaction, but it satisfies the constraints Mark is referring
> to (I will cover the description stuff later). The HTML Forms mechanism
> is the closest analog to what Hydra's Operation classes are trying to
> do. HTML forms work because they do the following:
> 
> * Identify a control for collecting information <form id="foo-form"
> action="http://example.com/foo" method="GET"></form>
> * Identify the data points to be collected via input elements, and how
> to format the message to send
> * An optional enctype value to describe how the data should be sent to
> server, but defaults to application/x-www-form-urlencoded
> * Defines what HTTP method will be used
> * The URL that will accept the data
> * An optional "accepts" attribute to declare the acceptable media types
> * Lastly, there is a control to activate the form, either a Submit
> button or Javascript trigger.

You forget the prose that's usually around those forms which tell the
incredible sophisticated agent (i.e., you) why he would want to submit that
form. Have you ever tried to fill out a form on a Chinese website? Were the
things you described above enough to know which form to choose? Sometimes
you can infer that from the "data points it collects" but in most cases that
won't work.


> Beyond that, HTML forms are pretty simple, yet remarkably successfully.
> Forms are also not extensible either, and that hasn't been much of a
> problem for developers. There's only a been one type of form element

Because they typically rely on JavaScript and JSON if they need more
functionality. Furthermore, things like nested structures etc. are
represented visually instead of doing so at the data level.


> and it's worked well over the years. There's also no sensible way of
> specializing the HTTP methods in HTML. The forms mechanism merely tells
> the browser how to format the request and what HTTP method to use. It
> makes no effort to add semantics to the HTTP method. If were took the
> following Hydra operation:
> 
> {
>   "@context": "http://www.w3.org/ns/hydra/context.jsonld",
>   "@id": "/an-issue/12",
>   "title": "An exemplary issue representation",
>   "operations": [
>     {
>       "@type": "DeleteResourceOperation",
>       "method": "DELETE"
>     }
>   ]
> }
> 
> And describe it using an HTML form, we might end up with something like
> this:
> 
> <form id="delete-issue
>            action="/an-issue/12"
>            method="DELETE">
> 	<input type="submit" id="submitDelete"/>
> </form>
> 
> If HTML forms supported the DELETE method, both options should
> successfully delete the resource at /an-issue/12 . Now if it was
> archived, I don't know this fact at submit time, but rather I learn
> this from the response.
> 
> Obviously, this is too simplistic/contrived of an example. Let's
> consider the following Hydra operation:
> 
> {
>   "@context": "http://www.w3.org/ns/hydra/context.jsonld",
>   "@id": "http://api.example.com/doc/#comments",
>   "@type": "Link",
>   "title": "Comments",
>   "description": "A link to comments with an operation to create a new
> comment.",
>   "supportedOperations": [
>     {
>       "@type": "CreateResourceOperation",
>       "title": "Creates a new comment",
>       "method": "POST",
>       "expects": "http://api.example.com/doc/#Comment",
>       "returns": "http://api.example.com/doc/#Comment",
>       "statusCodes": [
>         ... optional information about status codes that might be
> returned ...
>       ]
>     }
>   ]
> }
> 
> Now let's take a look this same operation using HTML forms:
> 
> <form id="comments-form"
>       action="http://api.example.com/comments"
>       method="POST"
>       enctype="application/x-www-form-urlencoded"
>       returnMediaType="application/ld+json"
>       expectedReturnType="http://api.example.com/doc/#Comment"
>       statusCodes="201, 401, 400">
> 
>       <input type="text" id="name"/>
>       ...
> </form>
> 
> If we had to bake in the expectations about what the return types could
> be and what types of potential status code the form action might
> return, HTML forms would be no way near as successful as they are
> today. What would a browser do if the form return HTML? If the server
> responded with a 403? A 302 even? Baking the post conditions into the
> form, or operation, lends it self to all kinds of coupling and gets
> developers to avoid looking at the HTTP response headers to determine
> what action to determine if/how they should handle the response, but
> rather rely solely on what the ApiDocumentation asserts. Intermediaries
> can't do anything with this information. At that point you're in
> WSDL/WADL/Swagger territory.

I completely agree with these points. Yet, developers usually want at least
to know what they can expect to get back on success. If you don't know that
it's incredible difficult to program against. Again, on the human Web that's
comparable simple as there's a very smart agent processing the responses.


> The fact that HTML doesn't concern the browser with things like returns
> types and potential response codes is one of the things that makes the
> web work today. Beating the horse a little more, consider a checkout
> process whereby one of my payment options use PayPal and I'm sending
> data via POST to PayPal's payment API and awaiting the response from
> PayPal. I'm going to send the client from my API in my domain, over to
> PayPal, where I don't have control over PayPal's API, more importantly
> their namespace or response codes. Using my ApiDocumentation to
> describe what I think is PayPal's expected response types is recipe for
> failure.

Then just leave it out :-) It's not required to specify these things.


> Now, without a doubt, HTML forms don't do much to describe what the
> form does. HTML rely's on the fact that a Human can parse the text on
> the page in order to determine the controls function. In Hydra, we're
> trying to get at the machine parseable analog to descriptive text. I

Exactly. The solution I chose was to type operations as I felt that's the
simplest solution that a lot of people will understand instinctively. How
would you describe it instead?


> guess I was trying to get at is that Hyrda should put it's effort in
> describing how to format the messages rather than through specialized
> Operations.
> 
> Hyrda is close, but the two things that bug me the most are the
> declarations in the operation that tell the client what to expect. What
> could be better is taking something like this:
> 
> {
>   "@context": "http://www.w3.org/ns/hydra/context.jsonld",
>   "@id": "http://api.example.com/doc/#comments",
>   "@type": "Link",
>   "title": "Comments",
>   "description": "A link to comments with an operation to create a new
> comment.",
>   "supportedOperations": [
>     {
>       "@type": "CreateResourceOperation",
>       "title": "Creates a new comment",
>       "method": "POST",
>       "expects": "http://api.example.com/doc/#Comment",
>       "returns": "http://api.example.com/doc/#Comment",
>       "statusCodes": [
>         ... optional information about status codes that might be
> returned ...
>       ]
>     }
>   ]
> }
> 
> And turning it into this:
> 
> {
>   "@context": "http://www.w3.org/ns/hydra/context.jsonld",
>   "@id": "http://api.example.com/doc/#comments",
>   "@type": "Link",
>   "title": "Comments",
>   "description": "A link to comments with an operation to create a new
> comment.",
>   "supportedOperations": [
>     {
>       "@type": "CreateResourceOperation",
>       "title": "Creates a new comment",
>       "method": "POST",
>       "expects": "http://api.example.com/doc/#Comment"
>       ]
>     }
>   ]
> }

That's completely valid and in most cases you will achieve exactly the same.
It doesn't, however, offer enough information to create a documentation
similar to those of most current Web APIs. I found that an important use
case to support.


> We can give hints as to what's in the response by leveraging Content-
> Type and Link headers [1]:
> 
> HTTP/1.1 200 OK
> ...
> Content-Type: application/ld+json
> Link: <http://api.example.com/doc/#Comment>; rel="profile";
> type="application/ld+json",
>       <http://www.w3.org/ns/hydra/context.jsonld>;
> rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"
> .
> 
> Or in the case that commenting went wrong and we have different
> response:
> 
> HTTP/1.1 400 Bad Request
> ...
> Content-Type: application/ld+json
> Link: <http://api.example.com/doc/#FormatError>; rel="profile";
> type="application/ld+json",
>       <http://www.w3.org/ns/hydra/context.jsonld>;
> rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"
> .
> 
> Or, in the case I deal all too often with misconfigured Tomcat hosted
> APIs:
> 
> HTTP/1.1 400 Bad Request
> ...
> Content-Type: text/html
> ...
> 
> Using Content-Type and Link headers, the client can determine if it can
> handle the response without having to parse the message body. You still
> have the same descriptive capabilities, but you're now telling the
> client to look at the response rather than looking at the
> documentation. This is kind of the point that Stu Chartlon was getting
> at in his "The trouble with APIs" post [2].

I think the biggest problem is how this stuff is documented and the fact
that people won't read it :-) It's not a contract. It's a hint. Clients have
to interpret the response in any case. Some will be more tolerant, some will
break when they don't get back what they expected. That's similar to how
people often run into troubles when parts of a website get redesigned and
"nothing works anymore" because it looks different or they have to take
different paths.

I have troubles extracting something actionable from your mails. Would
removing returns/statusCodes address your concerns? If so, what does it
really change? IMO it will be just a matter of time till someone else mints
a URL for these things in order to, again, be able to transform a Hydra
description into a nicely formatted HTML documentation.

 
> [1] http://words.steveklabnik.com/the-profile-link-relation-and-you
> [2] http://www.stucharlton.com/blog/archives/2011/06/the-trouble-with-
> apis.html


--
Markus Lanthaler
@markuslanthaler

Received on Monday, 3 February 2014 20:19:59 UTC