我在概念上很难理解有关版本化资源的URL的正确内容。
假设我有一个应用程序以非常类似于版本控制系统的方式跟踪食谱,就像RCS之类的旧学校一样。每个版本可以是一段时间的工作副本,然后从中创建一个新版本。每个版本都有与之关联的注释,不会共享注释。我可以随时回顾历史并查看配方的演变,但每个实例始终被视为同一配方的版本。我正在试图找出构建URL的最合适的方法来引用这些,并且我很难理解类似子资源和时间资源之间的一些差异等。
我见过这两件事的主要方式是:
> 1) query parameters
> -- /recipes/ultimate-thing -> List of available versions of Ultimate Thing
> -- /recipes/ultimate-thing?version=2 -> Version 2 of Ultimate Thing
> -- /recipes/ultimate-thing?version=latest -> Current working version of Ultimate Thing
2a) Nested resources with versions considered subresources
-- /recipes/ultimate-thing/versions/ -> List of available versions of Ultimate Thing
-- /recipes/ultimate-thing/versions/2 -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing -> Current working version of Ultimate Thing
2b) Nested resources with the list at the resource
-- /recipes/ultimate-thing -> List of available versions of Ultimate Thing
-- /recipes/ultimate-thing/versions/2 -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing/versions/latest -> Current working version of Ultimate Thing
我觉得每当我试图说服自己一种做法时,我觉得我缺乏理解为这种方法构建正确的理由。
1似乎是很多铁路人员的首选,但我不知道如何清楚我何时想要创建一个新版本的配方,因为POST到特定资源( /食谱/终极事物)通常需要创建具有该名称的资源(如果它不存在),是否适合返回类似404的内容而不是创建它并仅发布到 / recipes 创建新配方,然后允许POST / recipes / ultimate-thing 创建新版本?此外,如何表达评论?如果没有版本,我可以做一些像 / recipes / ultimate-thing / comments 这样的内容,但 / recipes / utlimate-thing / comments?version = latest 看起来很难看,而2b看起来似乎很难看更明确的“最终版食谱的最新版本的评论”,2a似乎是最干净的。
事实上,我一般都认为2a是最好的,但我很难确定版本是食谱的子资源,因为没有配方,版本 配方,但该配方的所有版本应该在逻辑上组合在一起。
我在stackoverflow上也看到2b,但似乎它可能过于冗长,因为你总是需要像 / recipes / ultimate-thing / version / latest / comments 这样的东西。虽然如果我认为某个版本是一个带有可以注释的指令的成分集合并且配方是一组具有相似意图的版本,这确实是最有意义的。
我真的很喜欢2a,但是我有什么方法可以解决这个问题吗?我是否遗漏了一些如何工作的东西,这使得它更有意义,它似乎被许多人所喜欢?
或者总是做一些像 / recipes / ultimate-thing / latest 或 / recipes / ultimate-thing / 2 / comments 这样的事情更有意义这是不明确的,这些是同一食谱的不同修订版?
我不确定这是否是一个非常适合的论坛,我只想讨论它,所以我理解为什么一种方法会比另一种更好。
答案 0 :(得分:8)
哇。好问题。我也发现REST最大的挑战之一就是风格,而不是严格的规范。这种风格似乎没有被充分理解,也没有什么可以强制执行它。
简短回答:风格#1: / recipes / cake?version = 2
这种风格似乎对我来说最合适。只有在快速谷歌搜索之后,才发现查询参数被认为是URI的一部分。因此,在URI中添加标识属性似乎是合适的。
样式2和3似乎违反了其他REST约定,其中路径段表示ID和子资源。因此,样式 / recipes / cake / 2 看起来像ID为2的“蛋糕”资源。< recipes / cake / version / 2 使“version”看起来像是一个子资源,实际上它是父资源的一个属性。
答案 1 :(得分:4)
我刚刚在去年年底实施了这种模式,并且遵循了2a和2b的组合:
2c) Nested resources with versions considered subsets
-- /recipes/ultimate-thing/versions/ -> List of available versions of Ultimate Thing
-- /recipes/ultimate-thing/versions/2 -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing/versions/latest-> Redirect to current working version of Ultimate Thing
不要将更长的URI视为“子资源”或“属性”,就像URI标识事物一样。将URI视为识别数据更为自然,并且,由于HTTP URI是分层的,因此较长的URI引用数据子集。集合中的项是数据:集合中所有数据的子集。 “事物”的“属性”是数据:关于该事物的所有数据的子集。 “子资源”是关于“事物”的数据,该事物不是独立于其父项存在的,因此是关于父项的所有数据的子集。
在你的情况下,你不必执行过多的精神柔道,将recipes/cherry-pie/
视为关于什么是樱桃派以及如何制作樱桃派的所有数据,而不是特定版本的说明。其中包括身份信息和其他非特定于版本的数据,还包括指向versions/
的链接,以及指向某人获得一个人的历史和有趣视频的链接。然后,URI recipes/cherry-pie/versions/
是版本化的所有樱桃派数据的子集;通常,它只是指向recipes/cherry-pie/versions/{version}/
形式的每个URI的链接集合。我还会在versions/
中添加一个指向recipes/cherry-pie/versions/latest/
的文档链接,该链接会临时重定向到recipes/cherry-pie/versions/182/
或最新版本。
这对客户来说非常自然。许多人绊倒了他们所看到的资源爆炸,并认为可怜的客户无法跟上。但是,如果您沿着自然用户工作流程边界划分资源,则不会出现问题。有些客户想要浏览一套食谱;有些人会深入研究一个食谱(无论版本如何);有些人会深入了解有多少版本,哪一个是最新版本,并将从中选择;有些人会向下钻取并检索单个版本。如果你想显示单个食谱或所有食谱的所有版本,你可能做错了(在极少数情况下你不是,设计另一个资源,如recipes/byname/pie/quickview
,它收集所有的数据并大量缓存。)
答案 2 :(得分:1)
这完全取决于你如何看待资源版本。这些只是用于维基中的历史跟踪,或者版本是单独的资源。在第一种情况下,EJK answer似乎是一个很好的解决方案。对于第二种情况,为资源版本/变体创建新的唯一标识符可能是另一种解决方案。 例如:
3) Resources with versions
-- /recipes/ -> List of available recipes versions
-- /recipes/ultimate-thing-version-2 -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing -> The "offical" version of Ultimate Thing
如果资源是彼此的变化,这是更优选的解决方案:
3) List of recipes
-- /recipes/ -> List of available recipes and variations
-- /recipes/ultimate-thing-with-sugar -> The Ultimate Thing recipe with sugar
-- /recipes/ultimate-thing-with-honey -> The Ultimate Thing recipe with honey
答案 3 :(得分:0)
我有一个用例,其中每个版本的创建时间至关重要。 API必须回答的常见问题是:
/recipes/ultimate-thing
的最新版本是什么?/recipes/ultimate-thing
是2019-01-01的最新版本?/recipes/ultimate-thing
的哪些版本是在2019-01-01和2019-01-31之间创建的?数字3在我看来似乎是查询参数的明显例子:
-- /recipes/ultimate-thing?versions_since=2019-01-01&versions_until=2019-01-31
对于2使用相同的方法可能不会受到伤害:
-- /recipes/ultimate-thing?version=2019-01-01
然后保持一致,并对1.使用相同的方法:
-- /recipes/ultimate-thing?version=latest
从这个角度来看,您的选择1)-使用查询参数-似乎是最自然的。