REST - 修改资源的一部分 - PUT或POST

时间:2010-02-05 15:41:29

标签: http rest http-headers httpverbs

我正在看到如何使用REST更新资源的一部分(例如状态指示器)的主题。

选项似乎是:

  1. 抱怨HTTP没有PATCH或MODIFY命令。然而,HTTP MODIFY verb for REST?上接受的答案很好地说明了为什么这不像看起来那么好。

  2. 将POST与参数一起使用并识别方法(例如,名为“action”的参数)。一些建议是使用自定义方法名称指定X-HTTP-Method-Override标头。这似乎导致了基于你正在尝试做的事情在实现中切换的丑陋,并且批评不是一种特别的REST方式来使用POST。实际上,采用这种方法开始感觉像是一个RPC类型的接口。

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

  4. 此时,我认为#3是最合理的选择。

    这是最佳做法还是反模式?还有其他选择吗?

6 个答案:

答案 0 :(得分:7)

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

  1. 更新一件事。这是一个PUT。备选方案3

  2. 在事物的历史记录中添加其他日志条目。此序列日志条目中的列表项是当前状态。这是一个POST。选项2。

  3. 如果您是数据仓库或函数式编程类型,您往往会对状态更改不信任,并喜欢将一段新的历史事实POST到静态的,不可变的事物中。这确实需要将事物与事物的历史区分开来;导致两个表。

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

    就个人而言,我发现我对可变对象和PUT的信任度越来越低(“错误纠正”除外)。 (即便如此,我认为旧的东西可以留在原地,新的东西也加入了对自己以前版本的引用。)

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

答案 1 :(得分:5)

选项3(PUT到一些独立的子资源)是你现在最好的选择,并且在主要资源本身上使用POST不一定是“错误的” - 尽管你可能不同意这取决于如何你想成为一个迂腐的人。

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

答案 2 :(得分:5)

HTTP 具有PATCH命令。它在Section 19.6.1.1 of RFC 2068中定义,并在draft-dusseault-http-patch-16中进行了更新,目前正在等待publication as RFC

答案 3 :(得分:1)

POST&模拟PATCH无法使用


在解释之前,可能值得一提的是使用POST进行一般更新没有任何问题(参见here)特别是:

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)这是对一个尴尬问题的实用回应。

答案 4 :(得分:1)

PATCH适用于 patch diff 格式。在那之前,它根本不是很有用。

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

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

这一切都取决于资源的粒度和缓存的预期结果。

答案 5 :(得分:0)

答案有点迟了,但我会考虑在这种情况下使用JSON Patch

它的核心是需要资源的两个副本(原始副本和修改过的副本),并对其进行区分。 diff的结果是描述差异的 patch操作数组。

一个例子:

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

有许多client libraries可以在世代上完成艰巨的任务