REST复合/复合/嵌套资源[关闭]

问题描述 投票:166回答:2

我试图围绕解决基于REST的API中的概念的最佳方式。不包含其他资源的平面资源没有问题。我遇到麻烦的地方是复杂的资源。

例如,我有一本漫画书的资源。 ComicBook有各种各样的属性,如authorissue numberdate等。

漫画书还有一份1..n封面列表。这些封面是复杂的对象。它们包含大量关于封面的信息:艺术家,日期,甚至是封面的64位编码图像。

对于GET上的ComicBook,我可以回复漫画,所有的封面,包括他们的base64'ed图像。获得一部漫画可能不是什么大不了的事。但是假设我正在构建一个客户端应用程序,希望在表格中列出系统中的所有漫画。 该表将包含ComicBook资源中的一些属性,但我们当然不希望显示表中的所有封面。返回1000本漫画书,每本漫画书都有多个封面,这将导致大量数据通过网络传输,在这种情况下,最终用户不需要这些数据。

我的直觉是让Cover成为一种资源,让ComicBook包含封面。所以现在Cover是一个URI。漫画书上的GET现在工作,而不是巨大的Cover资源,我们发回每个封面的URI,客户可以根据需要检索封面资源。

现在我在创建新漫画方面遇到了问题。当我创建一个Comic时,我肯定会想创建至少一个封面,事实上这可能是一个商业规则。 所以现在我被卡住了,我要么强迫客户执行业务规则,首先提交Cover,获取该封面的URI,然后POSTing列表中包含该URI的ComicBook,或者我在POST上的ComicBook采用不同的外观资源比它吐出来的。 POSTGET的传入资源是深层副本,其中传出的GETs包含对依赖资源的引用。

Cover资源在任何情况下都可能是必要的,因为我确信作为客户我想在某些情况下解决覆盖方向问题。因此,无论依赖资源的大小如何,问题都以一般形式存在。一般来说,如何处理复杂的资源而不强迫客户只是“知道”这些资源是如何组成的?

api rest resources nested-resources
2个回答
62
投票

@ray,优秀的讨论

@jgerman,不要忘记仅仅因为它是REST,并不意味着必须从POST设置资源。

您选择在任何给定的资源表示中包含的内容取决于您。

您单独引用的封面的情况仅仅是创建父资源(漫画书),其子资源(封面)可以交叉引用。例如,您可能还希望分别提供对作者,发布者,字符或类别的引用。您可能希望单独创建这些资源,也可以在漫画书之前创建这些资源,将其作为子资源引用。或者,您可能希望在创建父资源时创建新的子资源。

您的封面的具体情况稍微复杂一点,因为封面确实需要漫画书,反之亦然。

但是,如果您将电子邮件视为资源,并将发件人地址视为子资源,您显然仍可以单独引用该地址。例如,从地址获取所有内容。或者,使用先前的地址创建新邮件。如果电子邮件是REST,您可以很容易地看到许多交叉引用的资源可用:/ received-messages,/ draft-messages,/ from-addresses,/ to-addresses,/ addresses,/ subject,/ attachments,/ folders ,/ tags,/ categories,/ labels,et al。

本教程提供了交叉引用资源的一个很好的示例。 http://www.peej.co.uk/articles/restfully-delicious.html

这是自动生成数据的最常见模式。例如,您不会为新资源发布URI,ID或创建日期,因为这些是由服务器生成的。然而,当您获得新资源时,您可以检索URI,ID或创建日期。

您的二进制数据示例。例如,您希望将二进制数据作为子资源发布。获取父资源后,您可以将这些子资源表示为相同的二进制数据,或表示二进制数据的URI。

表单和参数已经与资源的HTML表示不同。发布导致URL的二进制/文件参数不是一个延伸。

当您获得新资源的表单(/ comic-books / new),或者获取表单来编辑资源(/ comic-books / 0 / edit)时,您要求提供表单特定的资源表示。如果将其发布到内容类型为“application / x-www-form-urlencoded”或“multipart / form-data”的资源集合中,则要求服务器保存该类型表示。服务器可以使用保存的HTML表示或其他任何方式进行响应。

您可能还希望允许将HTML,XML或JSON表示发布到资源集合,以用于API或类似目的。

您也可以在描述时代表您的资源和工作流程,同时考虑漫画书后发布的封面,但要求漫画书有封面。示例如下。

  • 允许延迟创建封面
  • 允许创建漫画书所需的封面
  • 允许交叉引用封面
  • 允许多个封面
  • 创建漫画书草稿
  • 创建漫画书封面草稿
  • 发布漫画书草稿

GET /漫画书 => 200好,获取所有漫画书。

GET /漫画书/ 0 => 200好,带封面(/ cover / 1,/ covers / 2)获取漫画书(id:0)。

GET /漫画书/ 0 /封面 => 200 OK,获取漫画书的封面(id:0)。

GET /封面 => 200 OK,获取所有封面。

GET / cover / 1 => 200 OK,用漫画书(/漫画书/ 0)获取封面(id:1)。

GET /漫画书/新 => 200 OK,获取表单以创建漫画书(表格:POST / draft-comic-books)。

POST / draft-comic-books 名称= foo 笔者=嘘声 出版商=咕 出版= 2011-01-01 => 302找到,位置:/ draft-comic-books / 3,重定向到封面(二进制)草稿漫画书(id:3)。

GET / draft-comic-books / 3 => 200好,获得有封面的漫画书(id:3)。

GET / draft-comic-books / 3 / cover => 200 OK,获取漫画书草稿(/ draft-comic-book / 3)。

GET / draft-comic-books / 3 / cover / new => 200好,获取表格为草稿漫画书创作封面(/ draft-comic-book / 3)(表格:POST / draft-comic-books / 3 / cover)。

POST / draft-comic-books / 3 / cover cover_type =前 cover_data =(二进制) => 302找到,位置:/ draft-comic-books / 3 / cover,重定向到漫画书草稿的新封面(/ draft-comic-book / 3 / covers / 1)。

GET / draft-comic-books / 3 /发布 => 200好,获取表格发布漫画书(id:3)(表格:POST /已发布 - 漫画书)。

发布/出版漫画书 名称= foo 笔者=嘘声 出版商=咕 出版= 2011-01-01 cover_type =前 cover_data =(二进制) => 302找到,位置:/ comic-books / 3,重定向到已出版的漫画书(id:3),带封面。


42
投票

将封面视为资源绝对符合REST的精神,特别是HATEOAS。所以,是的,对GEThttp://example.com/comic-books/1请求会给你一本书1的表示,其中的属性包括一组封面的URI。到现在为止还挺好。

你的问题是如何处理漫画创作。如果您的业务规则是一本书有0或更多封面,那么您没有问题:

POST http://example.com/comic-books

无盖漫画书数据将创建一个新的漫画书并返回服务器生成的id(假设它回来为8),现在你可以像这样添加封面:

POST http://example.com/comic-books/8/covers

在实体主体的封面。

现在你有一个很好的问题,如果你的业务规则说总是必须至少有一个封面会发生什么。以下是一些选择,您在问题中确定了第一个选项:

  1. 首先强制创建封面,现在基本上覆盖非依赖资源,或者将初始封面放在创建漫画书的POST的实体主体中。正如您所说,这意味着您要创建POST的表示形式将与您获取的表示形式不同。
  2. 定义主要,初始或首选或其他指定封面的概念。这可能是一个建模黑客,如果你这样做,就像调整你的对象模型(你的概念或商业模型)以适应技术。不是一个好主意。

你应该权衡这两个选择,而不是简单地允许无盖漫画。

你应该选择哪三种选择?不太了解你的情况,但回答一般的1..N依赖资源问题,我会说:

  • 如果您可以使用0..N作为RESTful服务层,那就太好了。如果至少需要一个,RESTful SOA之间的层可能会处理进一步的业务约束。 (不确定这看起来如何,但它可能值得探索......最终用户通常不会看到SOA。)
  • 如果你只是必须建模一个1..N约束,那么问问自己封面是否只是可共享的资源,换句话说,它们可能存在于漫画书以外的东西上。现在,它们不依赖于资源,您可以先创建它们,然后在POST中提供创建漫画书的URI。
  • 如果您需要1..N且封面仍然依赖,只需放松您的直觉以保持POST和GET中的表示相同,或使它们相同。

最后一项解释如下:

<comic-book>
  <name>...</name>
  <edition>...</edition>
  <cover-image>...BASE64...</cover-image>
  <cover-image>...BASE64...</cover-image>
  <cover>...URI...</cover>
  <cover>...URI...</cover>
</comic-book>

当您发布POST时,如果您拥有它们(从其他书籍中借用),则允许现有的uris,但也可以放入一个或多个初始图像。如果您要创建图书并且您的实体没有初始封面图像,请返回409或类似的回复。在GET上你可以返回URI ..

所以基本上你允许POST和GET表示“相同”,但你只是选择不在GET上“使用”封面图像,也不在POST上覆盖。希望有道理。

© www.soinside.com 2019 - 2024. All rights reserved.