9

我在这里阅读了很多关于 SO 的讨论,观看了Jon Moore 的演示文稿(顺便说一句,其中解释了很多)并阅读了 Roy Fielding 在 HATEOAS 上的博客文章,但在客户端设计方面我仍然感到有点茫然。

API 问题

现在,我只是返回带有表单/锚点和定义列表的 xhtml 来表示资源。以下片段详细说明了我如何布置表单/锚点/列表。

# anchors
<li class='docs_url/#resourcename'>
  <a rel='self' href='resource location'></a>
</li>

# forms
<form action='action_url' method='whatever_method' class='???'></form>

# lists
<dl class='docs_url/#resourcename'>
    <dt>property</dt>
    <dd>value</dd>
</dl>

我的问题主要是针对表格的。在 Jon 的演讲中,他记录了诸如 (add_location_form) 等表单类型以及它们所需的输入。我没有很多资源,但我正在考虑抽象表单类型(添加、删除、更新等),并且只是在文档中注意(添加、更新)您必须发送目标资源的有效表示并删除,您必须发送标识符。

问题 1:使用 HATEOAS 的概念,我们真的不应该让客户端“发现”表单(通过对它们进行分类添加、删除、更新等)并发送回我们提供给它们的所有数据吗?我在这里的真正问题(不是要讨论)是这是否遵循良好的做法?

客户问题

在 HATEOAS 之后,我们对资源的操作是可发现的,这将如何影响客户端代码(api 的消费者)及其 ui。遵循这些原则,UI 应该只显示可用的操作,这听起来很棒,但它是如何实现的?

我目前的方法是将响应解析为 xml 并使用 xpath 来查找在客户端开发时已知的操作(记录的表单类,即添加、删除、更新)并显示 ui 控件(如果它们可用)。

问题2:我的发现方式错了吗?还是就客户而言,这太神奇了(知道表单类)?这不是假设客户端知道每个资源可以使用哪些操作(这可能很好,因为它是创建客户端的某种原因,对吗?)并且应该记录操作(表单类)到资源的映射,或者只是记录表单类并允许客户(和客户开发人员)研究和发现它们?

我知道我无处不在,但任何洞察力都非常感谢。我将标记为很好地回答了这两个问题中的任何一个的回复。谢谢!

4

1 回答 1

7

不,你很准。

浏览器只是简单地渲染 HTML 有效负载并依赖人类来实际解释、查找含义并可能适当地填充表单。

到目前为止,机器客户端往往在“解释”部分做得很糟糕。因此,相反,开发人员必须提前做出决定,并以极其详细的方式指导机器客户端。

理想情况下,“智能”HATEOS 客户端将具有某些事实,并了解上下文,以便更好地将这些事实映射到服务需求。

因为这就是我们所做的,对吧?我们看到一个表格“哦,他们想要姓名、地址、信用卡号”。我们不仅知道“姓名”、“地址”和“信用卡”号码是什么意思,我们还可以直观地知道它们的意思是我的名字,或者信用卡上的人的名字,或者被运送的人的名字到。

机器在“直觉”部分也很容易失败。因此,作为开发人员,您可以按照您认为确定正确事实及其放置方式所必需的逻辑编写代码。

但是,回到理想的客户端,它会看到每个表单,“知道”字段想要什么,查阅其内部“事实”列表,然后正确填充请求的有效负载并最终发出请求。

您可以看到,一种微不足道且显然很脆弱的方法是将参数名称简单地映射到内部数据。当参数名称为“name”时,您可以将其硬编码为:firstName + " " + lastName。或者您可以考虑“知道”他们在谈论运输的实际 rel,并使用:shipTo.firstName + " " + shipTo.lastName。

随着时间的推移,理想情况下,您可以建立一个映射集合等,这样如果一个有效负载突然引入了一个新字段,而它恰好是您已经知道的一个字段,您也可以“自动”填写它而无需更改为客户端。

但简单的事实是,虽然可以做到这一点,但几乎没有做到。语义通常是模糊的,无论如何你每次都必须为每个新的有效载荷编写新的“直觉”,所以你也可以直接对有效载荷进行编码并完成它。

但是,关键是,尤其是关于 HATEOS,您不会将数据“强制”到服务器上。服务器会告诉你它想要什么,尤其是当他们给你表格的时候。

所以思考的过程不是“哦,如果我想要一张运输发票,我看到了,现在,他们想要名称、地址和订单号,他们想要它的 url 编码,他们想要它发送到http://example .com/shipping_invoice。所以我每次都会发送:name + "&" + address + "&" + orderNumber 到http://example.com/shipping_invoice。简单!”。

而你想要做的是“我看到他们想要一个姓名、地址和订单号。所以我要做的是对于每个请求,我会阅读他们的表格。我会检查他们每次想要什么字段。如果他们想要名字,我会给他们名字。如果他们想要地址,我会给他们地址。如果他们想要订单号,我会给他们订单号。如果他们有任何 PRE-POPULATED 字段(甚至是“隐藏”字段) ,我也会把它们发回去,假设我支持它,我会以他们要求的编码发送到我从 FORM 标签的 action 字段中获得的 URL。”。

您可以在前一种情况下看到,您假设他们每次都想要该有效负载。就像您对 URL 进行硬编码一样。而对于第二个,也许他们认为名称和地址是多余的,所以他们不再要求了。也许他们为您可能还不支持的新功能添加了一些不错的默认值。也许他们将编码更改为多部分?或者更改了端点 URL。谁知道。

您只能在编写客户端代码时发送您知道的内容,对吗?如果他们改变了事情,那么你只能做你能做的。如果他们添加字段,希望他们添加不需要的字段。但是如果他们破坏了界面,嘿,他们破坏了界面,你就会记录一个错误。你在那里能做的不多。

但是,您使用 HATEOS 部分越多,它们为您提供的功能就越多,因此您可以更加灵活:填写表格、正确跟踪重定向、注意编码和媒体类型,您的客户端变得越灵活。

最后,大多数人根本不会在他们的客户中这样做。他们对它们进行了硬编码,因为它很简单,并且他们假设后端的变化速度不够快而不重要,或者如果确实发生了这种变化,任何停机时间都是可以接受的,直到他们纠正了客户端。更典型的是,特别是对于内部系统,您只会收到一封来自开发人员的电子邮件“嘿,正在更改 XYZ API,它将于 3 月 1 日上线。请在集成测试期间更新您的客户并与发布团队协调。kthx”。

这就是现实。这并不意味着你不应该这样做,或者你不应该让你的服务器对更聪明的客户端更友好。请记住一个错误的客户端,它假设一切都不会使一个好的基于 REST 的系统失效。这些系统与糟糕的客户一起工作得很好。wget ftw,嗯?

于 2012-02-24T18:53:22.993 回答