8

所以 HTTP 规范说 HTTP PUT 和 DELETE 应该是幂等的。这意味着,对具有相同正文的相同 URL 的多个 PUT 请求不应在服务器上产生额外的副作用。多个 HTTP DELETE 也是如此,如果将 2 个或多个 DELETE 请求发送到同一个 URL,则第二个(或第三个等)请求不应返回指示资源已被删除的错误。

但是,在处理完 DELETE 之后对 URI 的 PUT 请求呢?它应该返回 404 吗?

例如,考虑以下请求按此顺序执行:

  • POST /api/items - 创建一个item资源,返回 HTTP 201 和 URI /api/items/6
  • PUT /api/items/6 - 更新与item#6相关的数据
  • PUT /api/items/6 - 只要请求正文与之前的 PUT 相同,就没有副作用
  • DELETE /api/items/6 - 删除item#6 并返回 HTTP 202
  • DELETE /api/items/6 - 没有副作用,还返回 HTTP 202
  • GET /api/items/6 - 现在将返回 404
  • PUT /api/items/6 -这里应该发生什么?404?409?别的东西?

那么,PUT 是否应该与 get 一致并返回 404,或者像@CodeCaster 建议的那样,409 是否更合适?

4

2 回答 2

11

RFC 2616,第 9.6 节,PUT:

POST 和 PUT 请求的根本区别体现在 Request-URI 的不同含义上。POST 请求中的 URI 标识将处理封闭实体的资源。该资源可能是一个数据接受进程,一个通往其他协议的网关,或者一个接受注释的单独实体。相比之下,PUT 请求中的 URI 标识了请求中包含的实体——用户代理知道 URI 的意图,服务器不得尝试将请求应用于其他资源。

和:

如果无法使用 Request-URI 创建或修改资源,则应给出反映问题性质的适当错误响应。

所以定义'合适'就是看400系列,说明有客户端错误。首先,我将消除不相关的:

  • 400 Bad Request:由于语法错误,服务器无法理解该请求。
  • 401 Unauthorized:请求需要用户认证。
  • 402 需要付款:此代码保留供将来使用。
  • 406 Not Acceptable:根据请求中发送的接受标头,请求标识的资源 [...] 不可接受。
  • 407 Proxy Authentication Required:此代码 [...] 表示客户端必须首先通过代理验证自己。
  • 408 Request Timeout:客户端在服务器准备等待的时间内没有产生请求。
  • 411 Length Required : 服务器拒绝接受没有定义 Content-Length 的请求。

那么,我们可以使用哪些?

403 禁止

服务器理解请求,但拒绝执行。授权将无济于事,并且不应重复请求。

这个描述实际上非常适合,尽管它通常用于与权限相关的上下文中(例如:您可能不会......)。

404 未找到

服务器未找到任何与请求 URI 匹配的内容。没有说明这种情况是暂时的还是永久性的。如果服务器通过一些内部可配置的机制知道旧资源永久不可用并且没有转发地址,则应该使用 410 (Gone) 状态代码。当服务器不希望确切地揭示请求被拒绝的原因或没有其他响应适用时,通常使用此状态代码。

这个也是,尤其是最后一行。

405 方法不允许

Request-URI 所标识的资源不允许使用 Request-Line 中指定的方法。响应必须包含一个 Allow 标头,其中包含所请求资源的有效方法列表。

没有我们可以响应的有效方法,因为我们现在不希望在这个资源上执行任何方法,所以我们不能返回 405。

409 冲突

响应 PUT 请求时最有可能发生冲突。例如,如果正在使用版本控制并且被 PUT 的实体包含对资源的更改,这些更改与早期(第三方)请求所做的更改相冲突,则服务器可能会使用 409 响应来指示它无法完成请求. 在这种情况下,响应实体可能会以响应 Content-Type 定义的格式包含两个版本之间差异的列表。

但这假设在 URI 上已经有一个资源(怎么可能有冲突呢?)。

410 走了

请求的资源在服务器上不再可用,并且不知道转发地址。预计这种情况将被视为永久性的。具有链接编辑能力的客户端应该在用户批准后删除对 Request-URI 的引用。如果服务器不知道或无法确定条件是否是永久的,则应该使用状态代码 404(未找到)。

这个也有道理。


我已经编辑了这篇文章几次,当它声称“使用 410 或 404”时它被接受了,但现在我认为 403 也可能适用,因为 RFC 没有说明 403 必须与权限相关(但它似乎是由流行的 Web 服务器以这种方式实现的)。我想我已经消除了所有其他 400 个代码,但请随时发表评论(在您投反对票之前)。

于 2012-10-29T16:02:40.903 回答
0

您的问题有一个未说明的假设前提,即资源必须存在才能使 PUT 成功。 这不是一个有效的假设。

规范的相关部分(RFC2616)说:

用户代理知道 URI 的意图,服务器不得尝试将请求应用于其他资源。

规范没有说,“引用的 URI 上的对象必须已经存在才能使对该 URI 的 PUT 成功。”


简单的示例是通过 REST 实现的网络商店。 GET返回给定路径处对象的表示,同时DELETE删除给定路径处的项目。这些很容易。但是 POST 和 PUT 并不难理解。POST可以做任何事情,但一种用法是POST在客户端指定的容器中创建一个对象,并让服务器返回该容器中新创建的对象的 URI。PUT更有限;它为服务器提供给定 URI 处对象的表示。该对象可能已经存在,也可能不存在。PUT不是 REPLACE 的同义词。

在我看来,409 或 410 对于 PUT 来说是错误的,除非容器本身——你要放入的东西不存在。

所以:

POST /container
   ==> returns 200 with `Location:/container/resource-12345`

PUT /container/resource-98928
   ==> returns 201 CREATED or 200 OK

PUT /this-container-does-not-exist/resource-22828282
   --> returns 400

Of course it is up to you, whether you'd like your server to allow these PUT semantics. But there's nothing in the spec that says you must not allow clients to provide the URI of the resource that he is PUTting.

于 2012-10-30T18:55:01.080 回答