【问题标题】:REST - Modify Part of Resource - PUT or POSTREST - 修改部分资源 - PUT 或 POST
【发布时间】:2011-01-13 13:54:20
【问题描述】:

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

选项似乎是:

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

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

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

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

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

【问题讨论】:

    标签: http rest http-headers httpverbs


    【解决方案1】:

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

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

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

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

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

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

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

    【讨论】:

    • 感谢您的回复。我应该对我使用的示例(即状态更新)更具选择性。我要解决的问题比状态更普遍。而且,在我正在更新的状态中,它实际上是一个更新,表明接收系统正在处理资源(如确认)。我将进行错误检查以定义该字段允许的状态转换。所以,真的,我的情况适合你的#1(原来的#3)。
    • 如果您的数据模型没有历史记录,那么更新是常见的后备计划。但是,如果您仍在构建,请考虑保留 all 历史记录。作为一种优化,复制事物本身的最新历史记录。
    • 很好的答案。问题:您是否认为PUT 更新资源状态并触发日志条目更改它是不正确的?由于GET 调用可以并且确实创建日志条目,并且已经证明日志记录是一个内部问题,因此登录PUT 不合适吗?这将允许保留PUT 的幂等性质,除非该状态已被来自另一个客户端的 API 调用更改,因此日志记录似乎适合于此。没有?
    【解决方案2】:

    选项 3(PUT 到某个单独的子资源)是您目前最好的选择,仅在主资源本身上使用 POST 不一定是“错误的” - 尽管您可能不同意,具体取决于如何迂腐的你想成为它。

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

    【讨论】:

      【解决方案3】:

      HTTP 确实有一个 PATCH 命令。定义在Section 19.6.1.1 of RFC 2068,更新在draft-dusseault-http-patch-16,目前等待publication as RFC

      【讨论】:

      • 在实践中,最好还是暂时坚持使用 POST,或者只是将您的资源拆分为子资源并放入这些资源中。
      • 缺少工具是否是个问题当然取决于您拥有的工具,对吧?因此,我建议您尝试一下,而不是事先取消该选项。
      • PATCH 解决了一个已经可以通过 POST 和一些常识解决的小问题 - 它几乎肯定会被滥用并损坏网络(以及您允许它进入的任何其他系统),所以我会避免它,这是不值得的。
      • “损害网络”?请详细说明。
      • 它鼓励一种模式,该模式将被应用于解决由糟糕设计(例如资源识别不佳)引起的问题,而不是仅在实际需要的地方使用。让人们远离 POST 的问题已经够多了,我不想考虑 PATCH 的发展方向。
      【解决方案4】:

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


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

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

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

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

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

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

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

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

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

      【讨论】:

      • 我们的服务需要对 SAP、Flex、Silverlight、Excel 等第三方产品开放。这意味着我们必须使用最低公分母技术——有一段时间我们没有能够使用 PUT,因为所有客户端技术仅支持 GET 和 POST - 不幸的是,这是一个现实。我认为使用 PUT 但表明我们确实想使用 PATCH 是一个合理的折衷方案。
      • PUT 是一个糟糕的选择,因为非安全动词之间的主要区别是幂等性。 PUT 是幂等的(就像 DELETE 一样),但 PATCH 是非幂等的(如 POST)-因此“重载”POST 更有意义......当然,“仅”使用 POST 可能是一个想法,因为它已经非常好用了,而您通过引入 PATCH 之类的方法所获得的实际“可见性提高”实际上是徒劳的,不值得付出努力或冒险。
      • 我觉得有用的区别在于,对我来说,直接 PATCH 到资源 URI 是很直观的。使用 POST,我更愿意发布到相关的“处理”资源,该资源代表我应用更新。我同意这没什么大不了的,但我不预见你会误用,所以我可以接受 :-)
      【解决方案5】:

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

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

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

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

      【讨论】:

      • 您可以使用服务器可以接受的任何格式的 PATCH。例如,我将它与x-www-form-urlencoded 一起使用
      【解决方案6】:

      答案有点晚了,但我会考虑使用JSON Patch 来处理这种情况。

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

      一个例子:

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

      有很多client libraries可以做一代人的重担

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-26
        • 2014-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-18
        • 2019-06-26
        • 2023-03-21
        相关资源
        最近更新 更多