可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I was doing some reading on REST this morning and I came across the HATEOAS principle ("hypermedia as the engine of application state").
Quoting the REST Wikipedia page:
Clients make state transitions only through actions that are dynamically identified within hypermedia by the server (e.g. by hyperlinks within hypertext). Except for simple fixed entry points to the application, a client does not assume that any particular actions will be available for any particular resources beyond those described in representations previously received from the server.
And Roy Fielding's blog:
...if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period.
I read this as: The client may only request state changes based on the actions made available from the body of the response from the server (the hypertext).
In an HTML world, this makes perfect sense. The client should only be able to request state changes (new actions/pages) based on the links made available to them through the hypertext (HTML).
When the resource is represented in other ways - such as JSON, XML, YAML etc. This is not so apparent.
Let's take an example "REST" JSON API:
I create a new resource (a new comment for example) by sending a POST request to
/comments.json? # with params...
The server responds with:
# Headers
HTTP/1.1 201 Created
Location: http://example.com/comments/3
Content-Type: application/json; charset=utf-8
... Etc.
# Body
{"id":3,"name":"Bodacious","body":"An awesome comment","post_id":"1"}
I know that I can now access this comment at the URI returned in the header: http://example.com/comments/3.json
When I visit http://example.com/comments/3.json I see:
{"id":3,"name":"Bodacious","body":"An awesome comment","post_id":"1"}
Suppose the API's documentation tells me that I can delete this comment by sending a DELETE request to the same URI. This is fairly common amongst "REST" APIs.
However:
The response from the server at GET http://example.com/comments/3.json
doesn't tell me anything about being able to delete the comment by sending a DELETE request. All it shows me is the resource.
That I can also DELETE a comment with the same URL is something the client knows through out-of-band information (the documentation) and is not discovered and driven by the response from the server.
Here, the client is assuming that the DELETE action (and possible others) are available for this resource and this information has not been previously received from the server.
Have I misunderstood HATEOAS or am I right in saying than an API matching the above description would not, in the strict sense, be a REST API?
I'm aware 100% adherence to REST is not always possible or the most pragmatic way to go. I've posted this question purely to satisfy my own curiosity about the theory behind REST, not for advice on real world best-practice.
回答1:
JSON as a hypermedia type doesn't define an identifier for application flow. HTML has link and form tag that that guide a user through a process.
If your application is only concerned with PUT, POST, DELETE, GET on a resource, your documentation could easily explain that.
However, if it were more complicated like adding a rebuttal to a comment and that rebuttal was a different resource then the comment you would need hypermedia type that would guide the consumer create the rebuttal.
You could use HTML/XHTML, Create your own 'bodacious+json' or use something else. Here are all the different media types
http://www.iana.org/assignments/media-types/index.html
I'm using HAL and it has a pretty active group. Here are links to it.
http://www.iana.org/assignments/media-types/application/vnd.hal+json
http://stateless.co/hal_specification.html
The book "Building Hypermedia APIs with HTML5 and Node" goes deep into hypermedia and media types. It shows how to create a media type for a specific or general purpose in XML or JSON.
回答2:
Jon Moore gave an excellent talk in Nov 2010 about the nuts and bolts of writing a truly RESTful (i.e. HATEOAS supporting) API and client. In the first part, he suggests the JSON is not a proper media type for REST because it lacks a commonly understood way of representing links and supported HTTP methods. He argues that good ol' XHTML is actually perfect for this since tools for parsing it (i.e. XPath) are readily available, it supports forms (think GET link templating and PUT, POST, and DELETE methods) and has a well-understood way of identifying hyperlinks, plus some other advantages mainly achieved through the ability to use the API with any standard web browser (eases the jobs for devs, QA, and support staff.)
The argument I'd always made prior to watching his talk is that JSON is so much lower of bandwidth consumer than any *ML language e.g. XML, HTML, XHTML. But by using terse XHTML where possible such as relative links instead of absolute ones (hinted at but not so evident in the example he uses throughout his talk), and by using gzip compression, this argument loses a lot of weight.
I realize efforts such as JSON-Schema and other RFC's are underway to try standardizing things in JSON, but in the meantime, Moore's talk convinced me to give XHTML a try.
回答3:
A RESTful solution would be to utilise the Allow-header to inform the client of the available methods/actions:
> GET /posts/1/comments/1 HTTP/1.1
> Content-Type: application/json
>
< HTTP/1.1 200 OK
< Allow: HEAD, GET, DELETE
< Content-Type: application/json
<
< {
< "name": "Bodacious",
< "body": "An awesome comment",
< "id": "1",
< "uri": "/posts/1/comments/1"
< }
Fielding's dissertation sets out two types of metadata: representation metadata; and resource metadata.
The Allow-header in HTTP/1.1 functions as resource metadata because it describes some property of a resource; i.e. the methods it allows.
By fully utilising the features provided by HTTP you eliminate the need for any out-of-bound information, and become more RESTful.
HATEOAS in a simple HTTP context describes how a client can navigate from one representation to another by following URIs using GET, whilst the Allow-header informs the client of the additional methods supported by the resource that generated the representation.
It's a neat design; the client asked for a representation, and additionally received a whole bunch of extra metadata about the resource that enables the efficient requesting of further representations.
I think the quote you have from the Wikipedia REST page is somewhat misleading in its choice of words and hasn't helped here (N.B. It has been improved since this question was asked).
All HTTP clients have to assume that a GET-method is likely to be available for the majority of resources. They do this because support for GET and HEAD are the minimum requirements for an HTTP/1.1 server. Without this assumption the web would not function. If a client can assume GET is available, then why not make other assumptions about common methods such as DELETE, or POST?
REST and HTTP aim to leverage the power of making assumptions about a basic set of methods in order to reduce the overall volume of requests on a network; if a request succeeds there's no need for further communication; but if a request fails with status '405 Method Not Allowed', then the client is immediately in receipt of the requests that could succeed via the Allow-header:
> ANNIHILATE /posts/1/comments/1 HTTP/1.1
> Content-Type: application/json
>
< HTTP/1.1 405 Method Not Allowed
< Allow: HEAD, GET, DELETE
< Content-Type: application/json
<
If the basic set of HTTP/1.1 methods aren't enough then you are free to define your own. However, it would be RESTful to solve problems using the available features of HTTP before defining new methods or putting metadata into the message-body.
回答4:
A fully discoverable JSON API that doesn't require any out-of-band knowledge as you put it so succinctly:
"That I can also DELETE a comment with the same URL is something the client knows through out-of-band information (the documentation) and is not discovered and driven by the response from the server."
...is completely possible. It just requires a simple standard and a client that understands the standard. Check out hm-json and the hm-json Browser project:
https://bitbucket.org/ratfactor/hm-json-browser/
As you can see in the demo, absolutely no out-of-band documentation is needed - only one entry point URI from which all other resources and their HTTP methods can be discovered by browsing.
By the way, HAL as mentioned in suing's answer is very, very close to your hypothetical requirements for HATEOAS. It's a great standard and it has a lot of cool ideas like embedded resources, but it has no way of informing the client about all available HTTP methods such as DELETE for a given resource.
回答5:
Another solid (and new as of May 2013) attempt at resolving HATEOAS for JSON can be found here:
JSON API: http://jsonapi.org/
回答6:
The premise of your question contains an often misunderstood aspect of REST – that the API response body entity be responsible for not only communicating the representational state of the requested resource but for also communicating the over-all state of the application the resource belongs to. These two things - resource state and application state are not the same thing.
The response entity body by definition provides you the state of the resource at a point in time. But a single resource is only one of many that comprises an application. Application state is the combined states of all in scope related resources – at any point in time – from the perspective of the application consumer - human or machine. To deliver this 'application state' a level 3 REST API make possible HATEOAS.
Since Hypertext is what most people mean when referring to the 'Hyper'media in HATEOAS, the special power of hypertext is it's ability to link to other media. Further, since most experience hypertext via HTTP/HTML this tends to lead many to think hyperlinks are only possible via an anchor tag or link tag within the body of a response entity - but this is not so.
If the transport protocol is HTTP then application state can and should be communicated via headers. Specifically, one or more 'Link' HEADERS with a 'rel' attribute to provide semantics. The Link HEADER along with the ALLOW header are the HTTP mechanisms for communicating what the next possible state transitions are and how to go about accessing them.
If you decide to not use these built-in mechanisms than your options are to try and communicate the application state by 'piggy-backing' on your resource state communication channel i.e. the response body, which leads to trying to devise some form of additional specification into the design of the resource itself.
When this is done - 'piggy-backing'- many run into content-type issues because the response body has to be specified by a MIME/Content-type like XML or JSON which means figuring out how to implement the HATEOAS mechanisms via some custom content-type specific format like custom XML tags or key:value pairs of nested object. You can do this, many do - e.g. see json-api suggestion above, but again HTTP already provides mechanisms for this.
I think it goes to us as humans always thinking we have to see or be able to click on these links as in the normal web use-case but we are talking about APIs that I can only assume are being built not for human consumption but for machine consumption - right? The fact that headers - which are there by the way as part of the response - are not visible in most human interfaces to HTTP i.e. browsers is not an issue with REST but rather an implementation limitation of the HTTP Agents on the market.
Hope this helps. BTW if you want a good human browser for APIs google 'Paw API Browser'