68

我有 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 的最“宁静/名词”方式是什么?

4

5 回答 5

83

由于 HTTP 中没有复制或克隆方法,因此您想要做什么完全取决于您。在这种情况下,aPOST似乎完全合理,但其他标准采取了不同的方法:

  • WebDAV 添加了一个COPY方法
  • Amazon S3 使用PUT没有正文和特殊x-amz-copy-source标头。他们称之为PUT Object - Copy.

这两种方法都假定您知道目标 URI。您的示例似乎缺少已知的目标 uri,因此您几乎必须使用 POST。您不能使用 PUT 或 COPY,因为您的创建操作不是幂等的。

如果您的服务定义POST /resources为“创建新资源”,那么为什么不简单地定义另一种方式来指定资源而不是作为 POST 的主体?例如POST /resources?source=/resources/10,一个空的身体。

于 2013-09-12T04:23:09.683 回答
15

弗朗西斯的答案是一个很好的答案,可能是您正在寻找的。话虽如此,它在技术上并不是 RESTful,因为(正如他在评论中所说)它确实依赖于客户端提供带外信息。由于问题是“什么是宁静的方式”而不是“什么是好方式/最好的方式”,这让我开始思考是否有RESTful解决方案。而且我认为接下来是一个 RESTful 解决方案,尽管我不确定它在实践中是否一定会更好。

首先,正如您已经确定的那样,GET 后跟 POST 是简单而明显的 RESTful 方式,但效率不高。所以我们正在寻找一种优化,如果它感觉比那个解决方案不那么自然,我们不应该太惊讶!

POST + sourceId 解决方案创建了一个特殊的 URL——它不指向资源,而是指向做某事的指令。每当您发现自己创建了类似的特殊 URL 时,都值得考虑是否可以通过简单地定义更多资源来解决这样做的需要。

我们想要复制的能力

resources/10

如果我们想出另一个资源怎么办:

resources/10/copies

...并且此资源的定义只是“作为资源/10 副本的资源的集合”。

定义了这个资源后,我们现在可以用不同的术语重新声明我们的复制操作——而不是说“我希望服务器复制资源/10”,我们可以说“我想向集合中添加一个新事物是资源/10" 的副本。

这听起来很奇怪,但它很自然地适合 REST 语义。例如,假设这个资源当前看起来像这样(我将在这里使用 JSON 表示):

[]

我们可以使用 POST 或 PATCH [1] 来更新它:

POST resources/copies/10
["resources/11"]

请注意,我们发送到服务器的只是关于集合的元数据,因此它非常有效。我们可以假设服务器现在知道从哪里获取要复制的数据,因为这是该资源定义的一部分。我们还可以假设客户端知道这会导致在“resources/11”处创建一个新资源,原因相同。

使用此解决方案,所有内容都被明确定义为资源,并且所有内容都有一个规范的 URL,并且客户端不需要任何带外信息。

归根结底,为了更加 RESTful 而采用这种奇怪的解决方案值得吗?这可能取决于您的个人项目。但是通过创建不同的资源来尝试以不同的方式解决问题总是很有趣的!

[1] 我不知道在“resources/10/copies”上允许 GET 是否有意义。显然,只要原始资源或其副本发生更改,该副本就不再是真正的副本,也不应该出现在此集合中。在实现方面,我不认为让服务器负担跟踪它的意义,所以我认为这应该被视为仅更新资源。

于 2017-09-16T22:19:19.450 回答
9

我认为POST /resources/{id}复制资源是一个很好的解决方案。

为什么?

  • POST /resources是创建新资源的默认 REST 标准
  • POST /resources/{id}在大多数 REST api 中应该是不可能的,因为该 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}
  • 副本将仅在同一资源上
  • 它是最类似 REST 的变体

你怎么看待这件事?

于 2018-08-08T13:10:56.447 回答
4

您想要创建特定资源的副本。在这种情况下,我的方法是使用以下端点: POST /resources/{id}/copy,阅读它“创建资源 {id} 的副本”

于 2019-03-27T15:22:14.057 回答
0

如果这对任何人有帮助,就会把它放在那里。
我们有一个类似的场景,我们提供“克隆虚拟机”作为扩展我们的 IaaS 产品的功能。因此,如果用户想要扩展,他们将不得不POST: /vms/vm101使用 request_body 来访问端点

{"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 
          }

和 3 个 vm101 的克隆,即。vm102、vm103 和 vm104 将被旋转。

于 2017-04-05T01:59:36.197 回答