【问题标题】:What is the proper HTTP method for modifying a subordinate of the named resource?修改命名资源的下属的正确 HTTP 方法是什么?
【发布时间】:2014-12-03 16:31:58
【问题描述】:

我正在创建一个 Web 客户端,其目的是通过向它们添加记录并从中删除记录来修改一组数据库表。它必须以原子方式进行,因此删除和插入都必须通过单个 HTTP 请求完成。显然,这是某种写操作,但我很难确定哪种方法合适。

POST 一开始似乎是对的,除了RFC 2616 specifies POST 请求必须描述指定资源的“新下属”。这不是我在这里所做的。

PUT 可用于对现有事物进行更改,因此这似乎是正确的,除了 RFC 2616 also specifies 指出“PUT 请求中的 URI 标识了请求中包含的实体 [.. .] 并且服务器不得尝试将请求应用于其他资源,”这会排除该方法,因为我的 URI 没有直接指定数据库表。

PATCH 似乎更接近了 - 现在我不是通过仅 部分 覆盖资源来作弊 - 但RFC 5789 makes it clear 这个方法,如 PUT,必须实际修改指定的资源通过 URI,而不是某个从属资源。

那我应该用什么方法呢?

或者,更广泛地说,为了其他用户的利益:

对于 X 的请求,你使用

  • POST创建X的新下属,
  • PUT 创建一个新的 X,
  • PATCH修改X。

但是如果要修改X的下属,应该使用什么方法呢?

【问题讨论】:

  • 第一件事。如果您想使用正确的 HTTP 方法(如 Restfull 应用程序),则不应有一个请求涉及多个资源(如果您将表视为资源)。如果所有这些更新代表 1 个单一资源的 1 个单一更改,那么您应该使用 PUT
  • 我认为从属资源是由多个表描述的聚合实体 - 它不是我可以指向的单一事物,而是位于数据库之上的概念抽象结构(通过为支持此抽象而编写的过程进行操作)。即使 URI 没有命名要更改的资源,您能否证明为什么 PUT 是正确的?
  • 为了澄清一点,被修改的实际资源 - 特定的数据库抽象 - 由请求正文中的参数指定。因此,URI 绝对不是它的唯一标识符,而是标识进行少量预处理的请求处理程序。
  • @PlínioPantaleão 单个 REST 请求影响许多资源并没有错,只要它们通过单个逻辑资源这样做。请记住,客户端处理的表示与服务器实际存储数据的方式无关。
  • @thecoshman 这就是我在假设每个表都代表一个资源的情况下提出的原因。我知道这没有必要是真的,但这是我对问题的理解

标签: rest http http-method


【解决方案1】:

开始.. 并非所有必须都是 REST。如果 REST 是你的锤子,那么一切都可能看起来像钉子。

如果您真的想符合 REST 理想,PATCH 是不可能的。你只是真的应该转移状态。

因此,此问题的常见“解决方案”是在您已有的资源之外工作,但要发明一种新资源来代表您希望执行的“交易”。此事务可以包含有关您正在按顺序执行的操作的信息,可能是原子的。

这使您可以PUT(或者可能是POST)交易,如果需要,还可以GET 交易的当前状态以查明它是否成功。

不过,在大多数设计中,这并不合适,您应该退回到 POST 并定义您在父级上执行的简单 rpc 样式操作。

【讨论】:

  • 不绑定到 REST 的观点很受欢迎 - 我真的只想研究符合的可行性,但最后我会使用一个该死的 GET,如果这看起来最好的话。但是你的帖子的其余部分对我来说不是很清楚。特别是:1) 我已经有一个专用于事务的资源 - 一个与我的数据库通信的 ASP 页面 - 我已经 能够 对它进行 PUT 或 POST 操作,但这对我没有帮助确定要使用的“正确”动词。 2)我不知道您所说的“rpc 样式操作”是什么意思,也不知道这如何等同于交易资源的替代品。
  • 我认为您需要更加具体才能获得具体答案,但我的一般观点是..您可能只想使用 POST 并使用 rpc 样式的操作我的意思是:正文可能包含有关操作/函数调用的信息,而不是简单的资源状态转移。
  • PATCH 无疑是解决此问题的 方法。您想对“数据库”资源进行部分修改。您的身体将类似于{ "delete":[ <record IDs> ], "create": [ <new record objects> ] }。您正在向该州发送PATCH,如果您愿意,可以发送一个增量。
  • @thecoshman 从纯粹的“最佳工作工具”的角度来看,您可能是对的。我的观点是,如果你在学术上坚持使用 REST,那么根本就不应该有 PATCH 操作。
  • @Evert 我真的很好奇你的理由。
【解决方案2】:

RFC 2616 已过时。请改为阅读 RFC 723*,尤其是 http://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#POST

【讨论】:

  • 呵呵,我不知道。我是否应该以此暗示,在 POST 的新的、更模糊的定义下,您主张使用这种方法来处理这类事情?
【解决方案3】:

首先,请允许我纠正您对这些方法的理解。

POST 就是创建一个全新的资源。您将一些数据发送到服务器,并期望得到一个响应,说明这个新资源是在哪里创建的。期望是,如果您将POST 更改为/things/,则新资源将存储在/things/theNewThing/。使用POST,您将其留给服务器来决定所创建资源的名称。发送多个相同的 POST 请求会产生多个资源,每个资源都有自己的“事物”和自己的 URI(除非服务器有一些额外的逻辑来检测重复项)。

PUT主要是关于创建资源的。 PUTPOST 之间的第一个主要区别是 PUT 让客户端控制 URI。一般来说,你并不真的想要这个,但这就是重点。 PUT 所做的另一件事,不是修改,如果您仔细阅读规范,它会声明您将 URI 中的任何资源替换为全新版本。这看起来像是在进行修改,但实际上只是在同一个 URI 上的全新资源。

PATCH 顾名思义,用于PATCHing 资源。您向服务器发送描述如何修改特定资源的数据。考虑一个巨大的资源,PATCH 允许您发送您希望更改的少量数据,而PUT 则需要您发送整个新版本。

接下来,考虑资源。您有一组表,每个表都有很多行,这相当于一组包含许多资源的集合。现在,您的问题是您希望能够原子地添加资源并同时删除它们。所以你不能只是POST 然后DELETE,因为这显然不是原子的。 PATCHing 表怎么可能...

{ "add": [
  { /* a resource */ },
  { /* a resource */ } ],
  "remove" : [ "id one", "id two" ] }

在那个主体中,我们已将数据发送到服务器,以在服务器中创建两个资源并删除两个资源。现在,这有一个缺点,那就是很难让客户知道发生了什么。这两个新资源的客户端没有“正确”的方式,204 created 有点像,但意味着有一个 one 新资源的 URI 的标头...但我们添加了二。遗憾的是,无论如何你都会遇到这个问题,HTTP simple 并不是为了一次处理多个资源而设计的。


交易资源

所以这是人们提出的常见解决方案,我认为它很臭。基本思想是您首先在服务器上POST/PUT 一个数据块编码您希望进行的交易。然后,您使用另一种方法来“激活”此交易。

等等……这是两个请求……它发送的数据与您通过PATCH 发送的数据相同,然后您为了以某种方式“激活”该事务而更加捏造 HTTP。更重要的是,我们现在有这个“交易”资源!我们甚至用它做什么?

【讨论】:

    【解决方案4】:

    我知道这个问题已经被问过一段时间了,但我想我应该自己对此提供一些评论。这实际上不是真正的“答案”,而是对thecoshman 答案的回应。不幸的是,我无法评论他的回答,这将是正确的做法,但我没有足够的“声誉”,这是一个奇怪(且不必要)的概念,恕我直言。

    那么,现在开始我对@thecoshman 的评论:

    您似乎质疑“事务性资源”的概念,但在您的回答中,我认为您可能误解了它们的概念。在您的回答中,您描述了您首先对资源和相关事务进行 POST,然后 POST 另一个资源以“激活”此事务。但我相信事务资源的概念有些不同。

    我举个简单的例子:

    在系统中,您有一个“客户”资源和他的地址,客户作为主要(或命名)资源,地址是从属地址。对于此示例,假设我们有一个 customerId 为 1234 的客户。到达该客户的 URI 将是 /api/customer/1234。那么,您现在如何只更新客户的地址而无需更新整个客户资源?您可以定义一个名为“updateCustomerAddress”的“事务资源”。这样您就可以将POST 更新的客户地址数据(JSON 甚至 XML)发送到以下 URI:POST /api/customer/1234/updateCustomerAddress。然后,该服务将创建这个新的事务资源以应用于 customerId=1234 的客户。创建事务资源后,调用将返回 201,尽管实际更改可能尚未应用于客户资源。所以后续的GET /api/customer/1234 可能会返回旧地址,或者已经是新的和更新的地址。这很好地支持了用于更新从属资源甚至命名资源的异步模型。

    我们将如何处理创建的事务资源?它将对客户端完全不透明,并在事务完成后立即丢弃。因此,调用实际上可能不会返回事务资源的 URI,因为它可能在客户端尝试访问它时已经消失了。

    如您所见,事务性资源不应要求对服务进行两次 HTTP 调用,而只需一次即可完成。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-20
      • 2019-02-12
      • 2011-05-20
      • 2018-08-16
      • 1970-01-01
      • 2018-06-05
      • 1970-01-01
      • 2014-05-31
      相关资源
      最近更新 更多