REST复杂/复合/嵌套资源

我试图围绕基于REST的API来解决概念的最佳方式。 不包含其他资源的平坦资源是没有问题的。 我陷入困境的是复杂的资源。

比如我有一个ComicBook的资源。 ComicBook拥有各种各样的属性,如作者,发行编号,date等。

漫画书也有1..n封面的清单。 这些封面是复杂的对象。 他们包含了很多有关封面,艺术家,date,甚至封面的64位编码图像的信息。

对于漫画书上的GET,我只能返回漫画,以及所有封面,包括base64的图片。 获得一个漫画可能不是一个大问题。 但是,假设我正在构build一个客户端应用程序,该应用程序想要在表中列出系统中的所有漫画。 该表将包含来自ComicBook资源的一些属性,但我们当然不希望显示表中的所有封面。 返回1000本漫画书,每一本都有多个封面,会导致大量的数据通过networking传递,在这种情况下最终用户不需要的数据。

我的本能是使Cover成为资源,ComicBook包含封面。 所以现在Cover是一个URI。 GET漫画现在工作,而不是巨大的封面资源,我们发回每个封面的URI和客户端可以检索封面资源,因为他们需要他们。

现在我在创build新漫画时遇到了问题。 当然我创作至less要有一个封面,其实这可能是一个商业规则。 所以现在我被困住了,我要么迫使客户强制执行业务规则,首先提交封面,获取封面的URI,然后在列表中发布带有该URI的漫画书,或者在漫画书上的POST采取不同的观点资源比吐出来。 POST和GET的传入资源是深度副本,其中传出的GET包含对从属资源的引用。

封面资源可能是必要的,因为我敢肯定,作为一个客户,我想在某些情况下解决封面的方向。 因此,不pipe依赖资源的大小如何,问题都以一般forms存在。 总的来说,您如何处理复杂的资源,而不会强迫客户“知道”这些资源是如何组成的?

@ray,优秀的讨论

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

你select包括在资源的任何给定的表示forms是由你决定的。

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

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

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

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

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

以二进制数据为例。 例如,您想要将二进制数据作为子资源进行发布。 当获得父资源时,可以将这些子资源表示为相同的二进制数据,或者表示二进制数据的URI。

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

当您获取新资源的表单(/ comic-books / new)或者获取表单以编辑资源(/ comic-books / 0 / edit)时,您需要获取特定于表单的资源表示forms。 如果使用内容types“application / x-www-form-urlencoded”或“multipart / form-data”将其发布到资源集合,则要求服务器保存该types表示。 服务器可以用保存的HTML表示来响应,或者不pipe。

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

也可以按照您的描述来表示您的资源和工作stream程,同时考虑到漫画书后张贴的封面,但需要漫画书封面。 示例如下。

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

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

GET /漫画/ 0
=> 200 OK,带封面的漫画书(id:0)(/ covers / 1,/ covers / 2)。

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

GET /覆盖
=> 200 OK,获取全部封面。

GET / covers / 1
=> 200 OK,用漫画(/ comic-books / 0)获取封面(id:1)。

GET /漫画书/新
=> 200 OK,Get form创build漫画书(表单:POST /漫画书)。

POST /漫画书
名称= foo
笔者=嘘声
出版商=咕
出版= 2011-01-01
=> 302find,位置:/ draft-comic-books / 3,redirect到带有封面的漫画书(id:3)(二进制)。

GET / draft-comic-books / 3
=> 200 OK,拿起封面漫画书(id:3)。

GET /漫画书/ 3 /封面
=> 200行,获取草稿漫画(/漫画书/ 3)。

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

POST /漫画书/ 3 /封面
cover_type =前
cover_data =(二进制)
=> 302find,位置:/漫画书/ 3 /封面,redirect到漫画书的新封面(/漫画书/ 3 /封面/ 1)。

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

POST /发表漫画书
名称= foo
笔者=嘘声
出版商=咕
出版= 2011-01-01
cover_type =前
cover_data =(二进制)
=> 302find,地点:/ comic-books / 3,redirect到带封面的漫画书(id:3)。

作为资源处理封面绝对是REST的精神,特别是HATEOAS。 所以是的,对http://example.com/comic-books/1GET请求会给你一个书1的表示,其中的属性包括一组用于封面的URI。 到现在为止还挺好。

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

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

带有封面的漫画书数据将创build一个新的漫画书,并返回服务器生成的ID(可以说它回来为8),现在你可以像这样添加封面:

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

与在实体的封面。

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

  1. 首先强制创build封面,现在基本上将封面设置为非依赖资源,或者将初始封面放置在创build漫画的POST实体主体中。 这就像你说的那样,意味着你创buildPOST的表示与你获得的表示有所不同。

  2. 定义主要的,首字母的或首选的或其他指定的封面的概念。 这可能是一个模型破解,如果你这样做,就像调整你的对象模型(你的概念或业务模型),以适应技术。 不是一个好主意。

你应该权衡这两个select,而不是简单地允许不露面的漫画。

你应该select三种select中的哪一种? 不知道你的情况太多,但回答一般的1..N依赖资源问题,我会说:

  • 如果你可以为你的RESTful服务层使用0..N,太好了。 如果至less需要一个,那么您的RESTful SOA之间的一层可能会处理进一步的业务约束。 (不知道如何看,但它可能是值得探讨….最终用户通常不会看到SOA。)

  • 如果你只是模拟一个1..N的约束,那么问问自己封面是否可以成为可共享的资源,换句话说,它们可能存在于漫画之外的东西上。 现在他们不是依赖资源,您可以先创build它们,并在您的POST中提供创build漫画书的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> 

当你发布你允许现有的uris,如果你有(从其他书籍借来的),但也放入一个或多个初始图像。 如果您正在创build一本书,并且您的实体没有初始封面图片,请返回409或类似的回复。 在GET上你可以返回URI

所以,基本上你可以让POST和GET表示“保持一致”,但是你只是select不使用GET的封面图像,也不要覆盖POST。 希望是有道理的。