我正试图围绕解决基于REST的API中的概念的最佳方式。不包含其他资源的平面资源没有问题。我遇到麻烦的地方是复杂的资源。
例如,我有一本漫画书的资源。 ComicBook
上有各种属性,例如author
,issue number
,date
等。
漫画书还有1..n
封面列表。这些封面是复杂的对象。它们包含大量关于封面的信息:艺术家,日期,甚至是封面的64位编码图像。
对于GET
ComicBook
我可以返回漫画,所有封面,包括他们的base64'ed图像。获得一部漫画可能不是什么大不了的事。但是假设我正在构建一个客户端应用程序,希望在表格中列出系统中的所有漫画
该表将包含ComicBook
资源中的一些属性,但我们当然不希望显示表中的所有封面。返回1000本漫画书,每本漫画书都有多个封面,这将导致大量数据流过网络,在这种情况下,最终用户不需要这些数据。
我的直觉是让Cover
成为资源,ComicBook
包含封面。所以现在Cover
是一个URI。漫画书上的GET
现在可以使用,而不是我们为每个封面发回一个URI的巨大Cover
资源,客户可以根据需要检索封面资源。
现在我在制作新漫画方面遇到了问题。当我创建Comic
时,我肯定想创建至少一个封面,事实上这可能是一个商业规则。
所以现在我陷入困境,我要么强迫客户执行业务规则,首先提交Cover
,获取该封面的URI,然后POST
使用该URI ComicBook
列表或POST
上的ComicBook
收集的资源与其吐出的资源不同。 POST
和GET
的传入资源是深层副本,其中传出的GET
包含对依赖资源的引用。
在任何情况下都可能需要Cover
资源,因为我确信作为客户端我想在某些情况下解决覆盖方向问题。因此,无论依赖资源的大小如何,问题都以一般形式存在。一般来说,如何处理复杂的资源而不强迫客户只是“知道”这些资源是如何组成的?
答案 0 :(得分:62)
答案 1 :(得分:42)
将封面视为资源绝对符合REST的精神,尤其是HATEOAS。所以是的,对GET
的{{1}}请求将为您提供第1册的表示,其中的属性包括一组用于封面的URI。到目前为止一切都很好。
您的问题是如何处理漫画创作。如果您的商家规则是书籍 0或更多,那么您就没有问题:
http://example.com/comic-books/1
用无盖漫画书数据将创建一个新的漫画书并返回服务器生成的id(假设它回来为8),现在你可以像这样添加封面:
POST http://example.com/comic-books
封面在实体中。
现在你有一个很好的问题,如果你的商业规则说总是必须至少有一个封面会发生什么。以下是一些选择,您在问题中确定了第一个选择:
首先强制创建封面,现在基本上覆盖非依赖资源,或者将初始封面放在创建漫画书的POST的实体主体中。正如您所说,这意味着您要创建POST的表示形式将与您获取的表示形式不同。
定义主要,初始或首选或其他指定封面的概念。这可能是一个建模黑客,如果你这样做,就像调整你的对象模型(你的概念或商业模型)以适应技术。不是一个好主意。
你应该权衡这两个选择,而不是简单地允许无盖漫画。
你应该选择哪三种选择?不太了解你的情况,但回答一般的1..N依赖资源问题,我会说:
如果您可以使用0..N作为RESTful服务层,那就太棒了。如果至少需要一个,RESTful SOA之间的层可能会处理进一步的业务约束。 (不确定这看起来如何,但可能值得探索......最终用户通常不会看到SOA。)
如果您只是必须建模1..N约束,那么问问自己封面是否只是可共享的资源,换句话说,它们可能存在于漫画书以外的其他内容中。现在它们不是依赖资源,您可以先创建它们,然后在POST中提供创建漫画书的URI。
如果您需要1..N并且封面仍然依赖,只需放松您的直觉以保持POST和GET中的表示相同,或使它们相同。
最后一项解释如下:
POST http://example.com/comic-books/8/covers
当您发布POST时,如果您拥有它们(从其他书籍中借用),则允许现有的uris,但也可以放入一个或多个初始图像。如果您要创建图书并且您的实体没有初始封面图像,请返回409或类似的回复。在GET上你可以返回URI ..
所以基本上你允许POST和GET表示“相同”,但你只是选择不在GET上“使用”封面图像,也不在POST上覆盖。希望这是有道理的。