RESTful API 设计、HATEOAS 和资源发现
HATEOAS 背后的核心思想之一是客户端应该能够从单个入口点 URL 开始,并发现所有公开的资源和可用的状态转换。虽然我可以完美地看到 HTML 和浏览器后面的人如何点击链接和“提交”按钮,但我还是被问到如何将这一原则应用于我(不)幸运处理的问题。
我喜欢 RESTful 设计原则在论文和教育文章中的呈现方式,这些文章都有意义,如何喝杯咖啡 就是一个很好的例子。我将尝试遵循惯例并提出一个简单且没有繁琐细节的示例。让我们看看邮政编码和城市。
问题 1
假设我想设计 RESTful api 通过邮政编码查找城市。我提出了嵌套在邮政编码中的名为“城市”的资源,以便 http://api.addressbook.com/zip_codes/02125/cities
上的 GET 返回包含两条记录的文档,这些记录代表多切斯特和波士顿。
我的问题是:如何通过 HATEOAS 发现这样的 url?在 http://api.addressbook.com/zip_codes
下公开所有 ~40K 邮政编码的索引可能是不切实际的。即使拥有 40K 项索引不是问题,请记住我已经编造了这个示例,而且还有更大规模的集合。
所以本质上,我想公开的不是链接,而是链接模板,如下所示:http://api.addressbook.com/zip_codes/{:zip_code}/cities
,这违背了原则并依赖于客户拥有的带外知识。
问题 2
假设我想公开具有某些过滤功能的城市索引:
http://api.addressbook.com/cities?name=X
上的GET 将仅返回名称匹配 < 的城市code>X。
http://api.addressbook.com/cities?min_population=Y
上的 GET 只会返回人口等于或大于Y
的城市。
当然,这两个过滤器可以一起使用:http://api.addressbook.com/cities?name=X&min_population=Y
。
在这里,我不仅想公开 url,还想公开这两个可能的查询选项以及它们可以组合的事实。如果客户端没有对这些过滤器的语义以及将它们组合成动态 URL 背后的原理的带外知识,这似乎根本不可能。
那么,HATEOAS 背后的原则如何帮助使如此琐碎的 API 真正实现 RESTful?
One of the core ideas behind HATEOAS is that clients should be able to start from single entry point URL and discover all exposed resources and state transitions available for those. While I can perfectly see how that works with HTML and a human behind a browser clicking on links and "Submit" buttons, I'm quizzed about how this principle can be applied to problems I'm (un)lucky to deal with.
I like how RESTful design principle is presented in papers and educational articles where it all makes sense, How to GET a Cup of Coffee is a good example of such. I'll try to follow convention and come up with an example which is simple and free from tedious details. Let's look at zip codes and cities.
Problem 1
Let's say I want to design RESTful api for finding cities by zip codes. I come up with resources called 'cities' nested into zip codes, so that GET on http://api.addressbook.com/zip_codes/02125/cities
returns document containing, say, two records which represent Dorchester and Boston.
My question is: how such url can be discovered through HATEOAS? It's probably impractical to expose index of all ~40K zip codes under http://api.addressbook.com/zip_codes
. Even if it's not a problem to have 40K item index, remember that I've made this example up and there are collections of much greater magnitude out there.
So essentially, I would want to expose not link, but link template, rather, like this: http://api.addressbook.com/zip_codes/{:zip_code}/cities
, and that goes against principles and relies on out-of-band knowledge possessed by a client.
Problem 2
Let's say I want to expose cities index with certain filtering capabilities:
GET on
http://api.addressbook.com/cities?name=X
would return only cities with names matchingX
.GET on
http://api.addressbook.com/cities?min_population=Y
would only return cities with population equal or greater thanY
.
Of course these two filters can be used together: http://api.addressbook.com/cities?name=X&min_population=Y
.
Here I'd like to expose not only url, but also these two possible query options and the fact that they can be combined. This seems to be simply impossible without client's out-of-band knowledge of semantics of those filters and principles behind combining them into dynamic URLs.
So how principles behind HATEOAS can help making such trivial API really RESTful?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我建议使用 XHTML 表单:
HTML 中缺少的是
form
的rel
属性。查看这篇文章:
另请查看 OpenSearch。
To reduce the number of request consider this response:
I suggest using XHTML forms:
What's missing in HTML is a
rel
attribute forform
.Check out this article:
Also check out OpenSearch.
To reduce the number of request consider this response:
我想到了这个解决方案,但我不确定我是否真的推荐它:返回描述端点的 WADL URL,而不是返回资源 URL。示例:
该示例是 CXF 根据以下 Java 代码自动生成的:
This solution comes to mind, but I'm not sure that I'd actually recommend it: instead of returning a resource URL, return a WADL URL that describes the endpoint. Example:
That example was autogenerated by CXF from this Java code:
在回答问题 1 时,我假设您的单一入口点是
http://api.addressbook.com/zip_codes
,其目的是使客户端能够遍历整个 zip 集合代码并最终检索与其相关的城市。在这种情况下,我将使 http://api.addressbook.com/zip_codes 资源返回到邮政编码第一页的重定向,例如:
http://api.addressbook .com/zip_codes?start=0&end=xxxx
这将包含一个“页面”的邮政编码链接(无论适合系统处理的数字,再加上下一页(和上一页)的链接如果有的话)
。如果需要的话,客户端可以抓取整个邮政编码列表。
每个页面中返回的 URL 看起来类似于:
http://api.addressbook.com/zip_codes/02125
然后它就可以了。问题在于决定是否在邮政编码 URL 返回的表示形式中包含城市信息,或者根据需要包含指向它的链接。
现在,客户端可以选择是否遍历整个邮政编码列表,然后进行请求。邮政编码(和然后是城市),或者请求邮政编码页面,然后请求深入到某个部分
In answer to question 1, I'm assuming your single entry point is
http://api.addressbook.com/zip_codes
, and the intention, is to enable the client to traverse the entire collection of zip codes and ultimately retrieve the cities related to them.In which case i would make the
http://api.addressbook.com/zip_codes
resource return a redirect to the first page of zip codes, for example:http://api.addressbook.com/zip_codes?start=0&end=xxxx
This would contain a "page" worth of zip code links (whatever number is suitable for the system to handle, plus a link to the next page (and previous page if there is one).
This would enable a client to crawl the entire list of zip codes if it so desired.
The urls returned in each page would look similar to this:
http://api.addressbook.com/zip_codes/02125
And then it would be a matter of deciding whether to include the city information in the representation returned by a zip code URL, or the link to it depending on the need.
Now the client has a choice whether to traverse the entire list of zip codes and then request the zipcode (and then cities) for each, or request a page of zip codes, and then request drill down to a parti
我遇到了同样的问题 - 所以我通过一个实际的例子来解决这两个问题(以及一些你还没有想到的问题)。 http://thereisnorightway.blogspot.com/2012 /05/api-example-using-rest.html?m=1
基本上,问题 1 的解决方案是更改表示形式(正如 Roy 所说,将时间花在资源上)。您不必返回所有 zip,只需让您的资源包含分页即可。举个例子,当您从新闻网站请求新闻页面时 - 它会为您提供今天的新闻,并链接到更多内容,即使所有文章可能都位于相同的 url 结构下,即 ...article/123 等
问题 2 是有点笨拙 - 在 http 中有一个名为 OPTIONS 的小命令,我在示例中使用它基本上反映了 url 的功能 - 尽管你也可以在表示中解决这个问题,但它只会更复杂。基本上,它返回一个显示资源功能的自定义结构(包括可选参数)。
让我知道你的想法!
I was running into these same questions - so I worked through a practical example that solves both of these problems (and a few you haven't thought of yet). http://thereisnorightway.blogspot.com/2012/05/api-example-using-rest.html?m=1
Basically, the solution to problem 1 is that you change your representation (as Roy says, spend your time on the resource). You don't have to return all zips, just make your resource contain paging. As an example, when you request news pages from a news site - it gives you todays news, and links to more, even though all the articles may live under the same url structure, I.e. ...article/123, etc
Problem 2 is a little ackward - there is a little used command in http called OPTIONS that I used in the example to basically reflect the url's capability - although you could solve this in the representation too, it would just be more complicated. Basically, it gives back a custom structure that shows the capabilities of the resource (including optional parameters).
Let me know what you think!
我感觉您跳过了书签网址。这是第一个网址,而不是获取城市或邮政编码的网址。
因此,您从 ab:=http://api.addressbook.com 开始,
第一个链接返回可用链接的列表。这就是网络的运作方式。您访问 www.yahoo.com,然后开始点击链接,但不知道它们去了哪里。
因此,从原始链接 ab: 您将得到其他链接,它们可能具有 REL 链接,解释应如何访问这些资源或可以提交哪些参数。
我们在设计系统时首先想到的是从书签页面开始,确定可以访问的所有不同链接。
我确实同意你关于“客户端对这些过滤器语义的带外知识”的看法,我很难相信机器可以适应现有的内容,除非它有一些先入为主的规范,例如 HTML。客户端更有可能是由了解所有可能性的开发人员构建的,然后对应用程序进行编码以“可能”期望这些链接可用。如果链接可用,则程序可以使用开发人员在操作资源之前实现的逻辑。如果它不存在,那么它就不会执行该链接。最后,在开始遍历应用程序之前布置可能的路径。
I feel like you skipped over the bookmark URL. That is the first url, not the ones to get cities or zip codes.
So you start at ab:=http://api.addressbook.com
This first link returns back a list of available links. This is how the web works. You go to www.yahoo.com and then you start clicking links not knowing where they go.
So from the original link ab: you would get back the other links and they could have REL links that explain how those resources should be accessed or what parameters can be submitted.
The first think we did when designing our systems is to start from the bookmark page and determine all the different links that could be accessed.
I do agree with you about the 'client's out-of-band knowledge of semantics of those filters' it's hard for me to buy that a machine can just adapt to what is there unless it had some preconceived specification like HTML. It's more likely that the client is built by a developer who knows all the possibilities and then codes the application to 'potentially' expect those links to be available. If the link is available then the program can use the logic the developer implemented prior to act the resource. If it's not there then it just doesn't execute the link. In the end possible paths are laid out prior to beginning to traverse the application.