用于资源的不同版本的RESTful URL

问题描述 投票:10回答:4

我在概念上理解有关版本化资源的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到特定资源(/ recipes / ultimate-thing)通常希望创建具有该名称的资源(如果它不存在),是否适合返回类似404的内容而不是创建它,并且仅发布到/ recipes以创建新配方然后允许POST到/ recipes / ultimate什么来创建一个新版本?此外,如何表达评论?没有版本我可以做/食谱/终极事物/评论,但/ recipes / utlimate-thing / comments?版本=最新看起来很丑,而2b看起来更明确“最新版本的最终版食谱的评论” ,2a似乎最干净。

事实上,我通常最喜欢2a,但我很难确定版本是食谱的子资源,因为没有食谱,版本是食谱,但该食谱的所有版本应该在逻辑上组合在一起。

我也在stackoverflow上看到2b,但似乎它可能过于冗长,因为你需要像/ recipes / ultimate-thing / version / latest / comments这样的东西。虽然这确实是最有意义的,如果我认为一个版本作为成分的集合与指令可以注释和配方是共享类似意图的版本的集合。

我真的很喜欢2a,但是我有什么方法可以解决这个问题吗?我是否遗漏了一些如何工作的东西,这使得它更有意义,它似乎被很多人所喜欢?

或者更有意义的是总是做/ recipes / ultimate-thing / latest或/ recipes / ultimate-thing / 2 / comments之类的事情,即使这使得它们最不明确,这些是同一食谱的不同修订版?

我不确定这是否是一个非常适合的论坛,我只想讨论它,所以我理解为什么一种方法比另一种方法更好。

rest restful-url restful-architecture
4个回答
8
投票

哇。好问题。我也发现REST最大的挑战之一就是风格,而不是严格的规范。这种风格似乎没有被充分理解,也没有什么可以强制执行它。

简答:Style#1:/ recipes / cake?version = 2

这种风格似乎对我来说最合适。只有在快速谷歌搜索之后,才发现查询参数被认为是URI的一部分。因此,在URI中放置标识属性似乎是合适的。

样式2和3似乎违反了其他REST约定,其中路径段表示ID和子资源。因此,style / recipes / cake / 2看起来像ID为2的“蛋糕”资源./ recipes / cake / version / 2使得“version”看起来像是一个子资源,而实际上它是父类的属性资源。


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或者最新版本。

这对客户来说非常自然。许多人绊倒了他们所看到的资源爆炸,并认为可怜的客户无法跟上。但是,如果您沿着自然用户工作流程边界划分资源,则不会出现问题。有些客户想要浏览一套食谱;有些人会深入研究一个食谱(无论版本如何);有些人会深入了解有多少版本,哪一个是最新版本,并将从中选择;有些人会向下钻取并检索单个版本。如果你想显示单个配方或所有配方的所有版本,你可能做错了(在极少数情况下你不是,设计另一个资源,如EJK answer,它收集所有数据并缓存它重)。


1
投票

这一切都取决于你如何看待资源版本。这些只是用于维基中的历史跟踪,或者版本是单独的资源。在第一种情况下,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

如果资源是彼此的变化,这是更优选的解决方案:

/recipes/ultimate-thing

0
投票

我有一个用例,每个版本的创建时间至关重要。 API必须回答的常见问题是:

  1. /recipes/ultimate-thing的最新版本是什么?
  2. 什么版本的/recipes/ultimate-thing是2019-01-01的最新版本?
  3. 在2019-01-01和2019-01-31之间创建了哪些版本的-- /recipes/ultimate-thing?versions_since=2019-01-01&versions_until=2019-01-31

在我看来,对于查询参数来说,这是一个明显的例子:

-- /recipes/ultimate-thing?version=2019-01-01

使用相同的方法2可能不会有害:

-- /recipes/ultimate-thing?version=latest

然后让我们保持一致并使用相同的方法1:

qazxswpoi

从这个角度来看,您的选项1) - 使用查询参数 - 似乎最自然。

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