17

关于如何使用 REST 仅更新资源的一部分(例如状态指示器),我看到了很多人在挥手。

选项似乎是:

  1. 抱怨 HTTP 没有 PATCH 或 MODIFY 命令。但是,关于 REST 的 HTTP MODIFY 动词的公认答案是什么?很好地说明了为什么这不像看起来那么好。

  2. 使用带有参数的 POST 并标识一个方法(例如,一个名为“action”的参数)。一些建议是使用自定义方法名称指定 X-HTTP-Method-Override 标头。这似乎会导致根据您正在尝试做的事情在实现中进行切换的丑陋,并且对不是使用 POST 的特别 RESTful 方式的批评持开放态度。事实上,采用这种方法开始感觉像是一个 RPC 类型的接口。

  3. 使用 PUT 覆盖表示要更新的特定属性的资源的子资源。实际上,这实际上是对子资源的覆盖,这似乎符合 PUT 的精神。

在这一点上,我认为#3 是最合理的选择。

这是最佳实践还是反模式?还有其他选择吗?

4

6 回答 6

7

查看状态更新有两种方法。

  1. 更新一件事。那是一个PUT。选项 3

  2. 在事物的历史记录中添加额外的日志条目。此日志条目序列中的列表项是当前状态。那是一个帖子。选项 2。

如果您是数据仓库或函数式编程类型,则倾向于不信任状态变化,并且喜欢将新的历史事实发布到静态、不可变的事物上。这确实需要将事物与事物的历史区分开来;通往两张桌子。

否则,您不介意“更新”来改变事物的状态,并且您对 PUT 感到满意。这不区分事物及其历史,并将所有内容保存在一张表中。

就个人而言,我发现我越来越不信任可变对象和 PUT(“纠错”除外)。(即便如此,我认为旧的东西可以留在原地,而新的东西可以参考以前的版本来添加。)

如果有状态变化,我认为应该有一个状态日志或历史记录,并且应该有一个 POST 来向该历史记录添加一个新条目。可能有一些优化来反映对象的“当前”状态,但这只是幕后优化。

于 2010-02-05T15:50:10.787 回答
5

HTTP确实有一个 PATCH 命令。它在RFC 2068 的第 19.6.1.1 节中定义,并在draft-dusseault-http-patch-16中进行了更新,目前正在等待作为 RFC 发布

于 2010-02-05T16:13:01.613 回答
5

选项 3(PUT 到一些单独的子资源)是你现在最好的选择,仅仅在主资源本身上使用 POST 不一定是“错误的”——尽管你可能不同意,这取决于你想要的迂腐程度关于它。

坚持使用 3 并使用更精细的子资源,如果您确实需要类似 PATCH 的行为 - 使用 POST。就个人而言,即使 PATCH 实际上最终成为一个可行的选择,我仍然会使用这种方法。

于 2010-02-05T15:50:22.317 回答
1

PATCH 适用于补丁差异格式。在那之前,它根本不是很有用。

至于您使用自定义方法的解决方案 2,无论是在请求中还是在标头中,不不不不不,这太糟糕了:)

只有两种方法是有效的,要么 PUT 整个资源,修改子数据,要么 POST 到该资源,或者 PUT 到子资源。

这完全取决于资源的粒度和缓存的预期结果。

于 2010-02-08T08:33:02.563 回答
1

可以在不可用的情况下发布和模拟 PATCH


在解释这一点之前,可能值得一提的是,使用 POST 进行一般更新并没有错(请参阅此处),特别是:

POST 仅在其他方法非常适合的情况下使用时才会成为问题:例如,检索应作为某种资源表示的信息 (GET)、完全替换表示 (PUT)

实际上,我们应该使用 PATCH 对复杂资源进行小幅更新,但它并没有我们想要的那么广泛可用。我们可以通过使用附加属性作为 POST 的一部分来模拟 PATCH。

我们的服务需要对 SAP、Flex、Silverlight、Excel 等第三方产品开放。这意味着我们必须使用最低公分母技术——有一段时间我们无法使用 PUT,因为只有 GET 和所有客户端技术都支持 POST。

我采用的方法是将“_method=patch”作为 POST 请求的一部分。好处是;

(a)在服务器端很容易处理——我们基本上是在假装 PATCH 可用

(b) 它向第三方表明我们没有违反 REST,而是解决了浏览器的限制。这也与几年前 Rails 社区处理 PUT 的方式一致,因此许多人应该可以理解

(c)当 PATCH 变得更广泛可用时,它很容易替换

(d) 这是对尴尬问题的务实回应。

于 2010-02-05T17:49:06.193 回答
0

答案有点晚了,但我会考虑在这样的场景中使用JSON Patch

在它的核心,它需要资源的两个副本(原始的和修改的),并对其执行差异。diff 的结果是一组描述差异的补丁操作。

一个例子:

[
  { "op": "replace", "path": "/baz", "value": "boo" },
  { "op": "add", "path": "/hello", "value": ["world"] },
  { "op": "remove", "path": "/foo" }
]

有许多客户端库 可以在生成过程中完成繁重的工作

于 2019-01-29T10:42:03.247 回答