超媒体API链接遍历与实用性

4

我一直在尝试构建一个基于超媒体的API。看起来一切都运行良好。比如当我获取/books/isbn/12313441213时,我会得到以下内容:

<book>
  <id>123</id>
  <name>Hypermedia APIs</name>
  <description>Basic api design techniques</description>
  <tags>
    <tag>rest</tag>
    <tag>api</tag>
    <tag>service</tag>
  </tags>
  <authors>
    <link rel="author" uri="/authors/id/22" />
    <link rel="author" uri="/authors/id/18" />
  </authors>
</book>

现在我可以从这个资源中遍历作者。当我获取/books/by/author/id/18时,会得到如下内容:

<books>
  <book id="123">
    <name>Hypermedia APIs</name>
    <link rel="self" uri="/books/id/123" />
  </book>
  <book id="191">
    <name>Chef Recipes for Rails Developers</name>
    <link rel="self" uri="/books/id/191" />
  </book>
  <book id="220">
    <name>Rails 4 Cookbook</name>
    <link rel="self" uri="/books/id/220" />
  </book>
  <book id="292">
    <name>Ruby 102</name>
    <link rel="self" uri="/books/id/292" />
  </book>
  <book id="432">
    <name>Semantic Architecture</name>
    <link rel="self" uri="/books/id/432" />
  </book>
  <book id="501">
    <name>Service Oriented Design</name>
    <link rel="self" uri="/books/id/501" />
  </book>
</books>

在我看来,这种uri模板化的方式也能很好地工作。无论如何,我的问题是关于如何实现像这样遍历链接的实用性?

考虑到你想要完整深度的资源(包括作者细节),你至少需要向服务器发出3个调用。同样,对于集合,你必须向服务器发送大量的调用。是的,也许我可以在这里利用资源扩展,但那么我为什么还要使用超媒体链接呢,因为我的所有客户端最终都将使用扩展资源。

我理解我们通过让客户端遍历链接而获得了很多好处(例如,如果客户端基于关系构建了资源发现,则当我们更改api或者他们被强制从资源端点本身获取最新架构时,它们受到的影响会最小,等等)。然而,这种方法的实用性或性能会使系统崩溃。

要么是我没有理解超媒体api设计中的某些东西,要么是超媒体api听起来很不错,但似乎只是一个理论上的想法,而不是一个实用的想法。

你有任何想法吗?

2个回答

2
这是一个有关所有时间的重要问题。实际上,另一个重要的问题是:谁知道遍历rels以到达特定资源R?
例如,要到达“book-detail”资源,客户端必须调用您的api条目,例如“/”,然后使用rel“book-categories”获取到book-categories的uri,然后调用OPTIONS查看该资源上是否可以进行GET操作,然后获取书籍类别,然后获取到“books-in-category”资源的rel ...等,最终到达“book-detail”资源。客户端应该知道到资源R的遍历路径。但不要硬编码URL,只需在运行时读取URL即可。这考虑了机器对人类的情况。
效率仍然存在。
在机器对机器的情况下,从“/”开始的自动机可以通过您的API并对您的资源进行某些处理。如果自动机需要访问树中的所有资源(或可能是状态机),则没有效率问题。但是,如果自动机需要调用树下的资源,并且它只知道API的入口点,则会遇到多次调用以达到其点的问题。
许多人会告诉您使用ETags和/或Memcached引入缓存,但缓存是为了增加性能,您不能100%依赖缓存,因为缓存会过期,因此在这些情况下,遍历资源R1到Rn的代码应该在您的客户端中。
请查看我的问题:https://stackoverflow.com/questions/15214526/why-hypermedia-api 但是回答您关于实用性的问题:如果您有一个业务工作流程,其中客户端需要转到资源R1,然后R2,然后... Rn,并且您想强制执行该流程,则超媒体是实用的。我假设没有直接调用Resource Rx而不经过R1-> R2-> ... Rx-1的情况。

启用私有缓存并在API的根资源上设置最大年龄就足以消除大量冗余调用。ETags和Memcached不是必需的。可以使用链接提示来避免需要进行OPTIONS往返。此外,客户端完全可以选择持久化书签以供将来使用,只要它能够处理书签在稍后某个时间点变得无效的后果。 - Darrel Miller
@DarrelMiller 这不仅仅是API的根资源,它完全取决于资源序列的深度。实际上,资源被结构化为状态机,但为了简单起见,我想将其视为一棵树。因此,您应该考虑从根资源到叶子资源的所有资源,并查看是否适合缓存;不仅根资源必须高效。请记住,在超媒体中,根资源并不一定会提供所有资源URL,它只会提供从根可达的内容。 - Rose
同意根并不是唯一需要缓存以优化效率的资源。然而,理想情况下,从根节点到叶节点的遍历应该尽可能短。此外,我发现大部分LO遍历可以映射到单个UI交互,因此没有浪费的往返传输。 - Darrel Miller

2

更传统的方法是使用参数:

GET /books?author=18

我希望响应看起来更像:

超媒体 API

您还可以使用参数指示要显示哪些子资源上的字段。类似于

GET /books/18?expand=author(name, birthday)

这将在响应中的作者标签中返回作者的姓名和生日。然后,您可以为用户请求书籍时获取多少作者信息提供合理的默认值(也许只是作者自己和姓名),但如果他们想要更多细节,则可以更改参数。

这些定制可以帮助减少需要进行的调用次数。

另一个观察结果是,很多数据都可以被缓存,无论是在客户端还是在中间代理上。书籍或作者的内容不太可能发生变化,因此这些资源可以让客户端长时间缓存它们。然后根本没有往返 - 客户端会使用自己的本地缓存获取数据。


谢谢你的回答,Eric。URI模板是我问题中有争议的部分。我认为根据您想在服务之上构建什么样的功能,两种情况都是有效的。至于您提到的资源扩展部分,我仍然困惑为什么会有人在这种情况下使用超媒体链接。我非常肯定很快虚拟客户端将采用完全扩展的资源。只有有意识的客户端才会明智地使用API。考虑到模式更新等因素,缓存也可能会受到惩罚。尽管如此,我仍然认为必须有更好的方法或表示方式...你不觉得吗? - James Dolnewiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接