18

HATEOAS 背后的核心理念之一是客户端应该能够从单个入口点 URL 开始,并发现所有可用的公开资源和状态转换。虽然我可以完美地看到它如何与 HTML 和浏览器后面的人点击链接和“提交”按钮一起工作,但我想知道如何将这一原则应用于我(不)幸运地处理的问题。

我喜欢 RESTful 设计原则在论文和教育文章中的呈现方式,这一切都很有意义,How to GET a Cup of Coffee就是一个很好的例子。我将尝试遵循惯例并提出一个简单且没有繁琐细节的示例。让我们看看邮政编码和城市。

问题 1

假设我想设计用于通过邮政编码查找城市的 RESTful api。我想出了嵌套在邮政编码中的称为“城市”的资源,因此 GET onhttp://api.addressbook.com/zip_codes/02125/cities返回包含例如代表多切斯特和波士顿的两条记录的文档。

我的问题是:如何通过 HATEOAS 发现这样的 url?公开所有 ~40K 邮政编码的索引可能不切实际http://api.addressbook.com/zip_codes。即使拥有 40K 项目索引不是问题,请记住我已经制作了这个示例,并且那里有更大数量的集合。

所以本质上,我想公开的不是链接,而是链接模板,而是像这样:http://api.addressbook.com/zip_codes/{:zip_code}/cities,这违背了原则,并且依赖于客户拥有的带外知识。

问题 2

假设我想公开具有某些过滤功能的城市索引:

  • GET onhttp://api.addressbook.com/cities?name=X将仅返回名称匹配的城市X

  • GET onhttp://api.addressbook.com/cities?min_population=Y只会返回人口等于或大于Y.

当然这两个过滤器可以一起使用:http://api.addressbook.com/cities?name=X&min_population=Y.

在这里,我不仅要公开 url,还要公开这两个可能的查询选项以及它们可以组合的事实。如果没有客户对这些过滤器的语义和将它们组合成动态 URL 背后的原则的带外知识,这似乎是根本不可能的。

那么 HATEOAS 背后的原则是如何帮助使这些微不足道的 API 真正成为 RESTful 的呢?

4

5 回答 5

4

我建议使用 XHTML 表单:

GET /

HTTP/1.1 OK

<form method="get" action="/zip_code_search" rel="http://api.addressbook.com/rels/zip_code_search">
   <p>Zip code search</p>
   <input name="zip_code"/>
</form>

GET /zip_code_search?zip_code=02125

HTTP/1.1 303 See Other
Location: /zip_code/02125

HTML 中缺少的relform.

看看这篇文章

总而言之,将 XHTML 视为 RESTful 服务的默认表示有几个原因。首先,您可以利用重要元素(如<a><form>、 )的语法和语义,而<input>不是自己发明。其次,您最终会得到感觉很像网站的服务,因为用户和应用程序都可以浏览它们。XHTML 仍然由人解释——它只是开发期间的程序员,而不是运行时的用户。这简化了整个开发过程,并使消费者更容易了解您的服务是如何工作的。最后,您可以利用标准 Web 开发框架来构建您的 RESTful 服务。

另请查看OpenSearch


要减少请求的数量,请考虑以下响应:

HTTP/1.1 200 OK
Content-Location: /zip_code/02125

<html>
<head>
<link href="/zip_code/02125/cities" rel="related http://api.addressbook.com/rels/zip_code/cities"/>
</head>
...
</html>
于 2012-02-01T19:48:53.237 回答
2

想到了这个解决方案,但我不确定我是否真的会推荐它:不是返回资源 URL,而是返回描述端点的 WADL URL。例子:

<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <grammars/>
  <resources base="http://localhost:8080/cities">
    <resource path="/">
      <method name="GET">
        <request>
          <param name="name" style="query" type="xs:string"/>
          <param name="min-population" style="query" type="xs:int"/>
        </request>
        <response>
          <representation mediaType="application/octet-stream"/>
        </response>
      </method>
    </resource>
  </resources>
</application>

该示例是由 CXF 从以下 Java 代码自动生成的:

import javax.ws.rs.GET;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

public class Cities {
    @GET
    public Response get(@QueryParam("name") String name, @QueryParam("min-population") int min_poulation) {
        // TODO: build the real response
        return Response.ok().build();
    }
}
于 2012-02-01T19:03:40.460 回答
2

在回答问题 1 时,我假设您的单一入口点是http://api.addressbook.com/zip_codes,其目的是使客户端能够遍历整个邮政编码集合并最终检索与它们相关的城市。

在这种情况下,我会让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 返回的表示中包含城市信息,还是在链接中包含城市信息。

现在客户端可以选择是遍历整个邮政编码列表,然后请求每个邮政编码(然后是城市),还是请求一个邮政编码页面,然后请求向下钻取到一个部分

于 2012-04-17T22:30:44.910 回答
1

我遇到了同样的问题——所以我通过一个实际的例子来解决这两个问题(还有一些你还没有想到的)。http://thereisnorightway.blogspot.com/2012/05/api-example-using-rest.html?m=1

基本上,问题 1 的解决方案是改变你的表现形式(正如 Roy 所说,把时间花在资源上)。您不必返回所有 zip,只需让您的资源包含分页。例如,当您从新闻站点请求新闻页面时 - 它会为您提供今天的新闻以及指向更多内容的链接,即使所有文章可能都位于相同的 url 结构下,例如 ...article/123 等

问题 2 有点不妥——在 http 中有一个名为 OPTIONS 的小命令,我在示例中使用它来基本反映 url 的功能——虽然你也可以在表示中解决这个问题,但它只会更复杂。基本上,它返回一个自定义结构,显示资源的功能(包括可选参数)。

让我知道你的想法!

于 2012-06-12T04:34:59.523 回答
0

我觉得你跳过了书签 URL。这是第一个网址,而不是获取城市或邮政编码的网址。

所以你从 ab:=http://api.addressbook.com 开始

第一个链接返回可用链接的列表。这就是网络的工作方式。你去 www.yahoo.com 然后你开始点击不知道去哪里的链接。

因此,从原始链接 ab:您将返回其他链接,它们可能具有 REL 链接,这些链接解释了应如何访问这些资源或可以提交哪些参数。

我们在设计系统时所做的第一个想法是从书签页面开始并确定可以访问的所有不同链接。

我同意你关于“客户对这些过滤器语义的带外知识”的看法,我很难相信一台机器可以适应现有的东西,除非它有一些像 HTML 这样的先入为主的规范。客户端更有可能是由了解所有可能性的开发人员构建的,然后对应用程序进行编码以“可能”期望这些链接可用。如果链接可用,则程序可以使用开发人员在执行资源之前实现的逻辑。如果它不存在,那么它只是不执行链接。最后,在开始遍历应用程序之前布置可能的路径。

于 2012-02-01T21:41:15.983 回答