我有REST API,它暴露了一个复杂的大型资源,我希望能够克隆这个资源。假设资源在/resources/{resoureId}
要克隆资源10我可以做类似的事情。
GET /resources/10
POST /resources/
put的正文,其中包含GET /resources/10
没有ID的代表副本,以便POST
创建新资源。这种方法的问题在于资源非常庞大和复杂,将完整的表示返回给客户端然后让客户端将其发送回来真的没有意义,因为这只会浪费带宽,服务器上的cpu。克服服务器上的资源非常容易,所以我想这样做。
我可以执行POST /resources/10/clone
或POST resources/clone/10
之类的操作,但这两种方法都是错误的,因为URL中的动词。
构建可以在这种情况下使用的url的最“安静/名词”方式是什么?
答案 0 :(得分:56)
由于HTTP中没有复制或克隆方法,因此您可以自行决定要执行的操作。在这种情况下,POST
似乎完全合理,但其他标准采取了不同的方法:
COPY
method。PUT
没有正文和特殊x-amz-copy-source
标题。他们称之为PUT Object - Copy
。这两种方法都假设您知道目标URI。您的示例似乎缺少已知的目标uri,因此您几乎必须使用POST。您不能使用PUT或COPY,因为您的创建操作不是幂等的。
如果您的服务将POST /resources
定义为“创建新资源”,那么为什么不简单地定义另一种方式来指定除POST作为主体之外的资源?例如。 POST /resources?source=/resources/10
空身。{/ p>
答案 1 :(得分:5)
弗朗西斯的回答很棒,可能就是你要找的。话虽如此,它在技术上并不是RESTful(因为他在评论中说)它确实依赖于客户端提供带外信息。既然问题是“什么是宁静的方式”而不是“什么是好方法/最好的方式”,这让我想到是否是一个RESTful解决方案。我认为接下来是一个RESTful解决方案,虽然我不确定它在实践中是否更好。
首先,正如您已经确定的那样,GET后跟POST是简单而明显的RESTful方式,但效率不高。所以我们正在寻找一个优化,如果它感觉比那个解决方案更自然,我们不应该太惊讶!
POST + sourceId解决方案创建一个特殊的URL - 一个不指向资源的URL,而是指向执行某些操作的指令。每当你发现自己创建这样的特殊URL时,有必要考虑是否可以通过简单地定义更多资源来解决这个问题的需要。
我们希望能够复制
resources/10
如果我们想出另一种资源怎么办:
resources/10/copies
...而且该资源的定义只是“资源的集合,是资源/ 10的副本”。
通过定义此资源,我们现在可以用不同的术语重新声明我们的复制操作 - 而不是说“我希望服务器复制资源/ 10”,我们可以说“我想在集合中添加新东西”作为资源副本的东西/ 10“。
这听起来很奇怪,但它自然适合REST语义。例如,假设此资源目前看起来像这样(我将在这里使用JSON表示):
[]
我们可以使用POST或PATCH [1]更新它:
POST resources/copies/10
["resources/11"]
请注意,我们发送到服务器的所有内容都是关于集合的元数据,所以它非常有效。我们可以假设服务器现在知道在哪里获取要复制的数据,因为这是该资源定义的一部分。我们还可以假设客户端知道这导致在“资源/ 11”处创建新资源的原因相同。
使用此解决方案,所有内容都明确定义为资源,并且所有内容都有一个规范URL,并且客户端不需要任何带外信息。
最终,为了更加RESTful,这种奇怪的解决方案是否值得去?这可能取决于您的个人项目。但是,通过创建不同的资源来尝试以不同方式构建问题总是很有趣!
[1]我不知道在“资源/ 10 /份”上允许GET是否有意义。显然,只要原始资源或其副本发生变化,副本就不再是副本,不应该在此集合中。在实现方面,我没有看到加重服务器负担的重点,所以我认为这应该被视为仅更新资源。
答案 2 :(得分:4)
我认为POST /resources/{id}
是复制资源的好方法。
为什么?
POST /resources
是创建新资源的默认REST标准POST /resources/{id}
应该是不可能的,因为该ID已经存在-您将永远不会与您(客户端)一起定义ID来生成新资源。服务器将定义ID。还请注意,您将永远不会在资源B上复制资源A。因此,如果您要复制id = 10的现有资源,则一些答案提示了这种情况:
POST /resources?sourceId=10
POST /resources?copyid=10
但这比较简单:
POST /resources/10
创建10的副本-您必须从存储中检索10,因此,如果找不到它,则无法复制=抛出404 Not Found。
如果确实存在,则创建它的副本。
因此,使用此想法,您可以看到将以下b资源复制到某些a资源中没有意义:
POST /resources?source=/resources/10
POST /resources-a?source=/resources-b/10
那么为什么不简单地使用POST / resources / {id}
{id}
您对此有何看法?
答案 3 :(得分:1)
如果这对任何人都有帮助的话,那就把它放在那里 我们有类似的情况,我们提供“clone vm”作为扩展我们的IaaS产品的功能。因此,如果用户想要扩展,则必须使用request_body
来命中POST: /vms/vm101
端点
{"action": "clone", // Specifies action to take, since our users can do couple of other actions on a vm, like power_off/power_on etc.
"body": {"name": [vm102, vm103, vm104] // Number of clones to make
"storage": 50, ... // Optional parameters for specifying differences in specs one would want from the base virtual machine
}
和vm101的3个克隆即。 vm102,vm103和vm104将被旋转。
答案 4 :(得分:0)
您要创建特定资源的副本。在这种情况下,我的方法是使用以下端点:
POST /resources/{id}/copy
,将其读取为“创建资源{id}的副本”