资源已存在时POST的HTTP响应代码

问题描述 投票:668回答:15

我正在构建一个允许客户端存储对象的服务器。这些对象在客户端完全构造,完整的对象ID对于对象的整个生命周期是永久的。

我已经定义了API,以便客户端可以使用PUT创建或修改对象:

PUT /objects/{id} HTTP/1.1
...

{json representation of the object}

{id}是对象ID,因此它是Request-URI的一部分。

现在,我也在考虑允许客户端使用POST创建对象:

POST /objects/ HTTP/1.1
...

{json representation of the object, including ID}

由于POST意味着“追加”操作,我不知道如果对象已经存在该怎么办。我应该将请求视为修改请求还是应该返回一些错误代码(哪个)?

http rest
15个回答
834
投票

我的感觉是409 Conflict是最合适的,然而,当然在野外很少看到:

由于与资源的当前状态冲突,无法完成请求。此代码仅在预期用户可能能够解决冲突并重新提交请求的情况下才允许。响应主体应该包含足够的信息供用户识别冲突的来源。理想情况下,响应实体将包含足够的信息供用户或用户代理解决问题;但是,这可能是不可能的,也不是必需的。

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


5
投票

我会选择422 Unprocessable Entity,当请求无效但问题不在于语法或身份验证时使用。

作为反对其他答案的论据,使用任何非4xx错误代码意味着它不是客户端错误,而且显然是。使用非4xx错误代码来表示客户端错误根本没有任何意义。

似乎409 Conflict是这里最常见的答案,但是,根据规范,这意味着资源已经存在,并且您应用于它的新数据与其当前状态不兼容。如果您要发送POST请求(例如,已使用的用户名),则它实际上并不与目标资源冲突,因为目标资源尚未过帐。当存储的资源版本与请求的资源版本之间存在冲突时,这是一个专门用于版本控制的错误。它对于此目的非常有用,例如,当客户端缓存旧版本的资源并根据不再有条件有效的不正确版本发送请求时。 “在这种情况下,响应表示可能包含有助于根据修订历史合并差异的信息。”使用该用户名创建另一个用户的请求是无法处理的,与版本控制无关。

对于记录,422也是当您尝试使用已在使用的名称创建存储库时GitHub使用的状态代码。


5
投票

这完全取决于上下文,以及谁负责在请求中重复(服务器或客户端或两者)

如果服务器只是指向副本,请查看4xx:

  • 400 Bad Request - 当服务器不处理请求时,因为它显然是客户端故障
  • 409 Conflict - 如果服务器不处理请求,但原因不是客户端的错误
  • ...

对于重复的隐式处理,请查看2XX:

  • 200好的
  • 201创建
  • ...

如果服务器需要返回一些内容,请查看3XX:

  • 302找到
  • 303见其他
  • ...

当服务器能够指向现有资源时,它意味着重定向。

如果上述还不够,那么在响应正文中准备一些错误消息总是一个好习惯。


4
投票

另一种可能的治疗方法是使用PATCH。 PATCH被定义为改变内部状态并且不限于附加的东西。

PATCH允许您更新现有项目,从而解决问题。见:RFC 5789: PATCH


2
投票

208 - http://httpstatusdogs.com/208-already-reported怎么样?这是一个选择吗?

在我看来,如果唯一的东西是重复资源,则不应该引发错误。毕竟,客户端或服务器端都没有错误。


2
投票

在检查重复记录的正确代码时偶然发现了这个问题。

原谅我的无知,但我不明白为什么每个人都忽略了代码“300”,清楚地说“多选”或“暧昧”

在我看来,这将是构建非标准或特定系统供您自己使用的完美代码。我也错了!

https://tools.ietf.org/html/rfc7231#section-6.4.1


2
投票

更有可能的是400 Bad Request

6.5.1. 400 Bad Request


The 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

由于请求包含重复值(已存在的值),因此可将其视为客户端错误。需要在下次尝试之前更改请求。 通过考虑这些事实,我们可以得出HTTP状态400错误请求。


70
投票

根据RFC 7231,如果处理POST的结果等同于现有资源的表示,则可以使用303 See Other。


66
投票

我个人使用WebDAV扩展422 Unprocessable Entity

According to RFC 4918

422 Unprocessable Entity状态代码表示服务器理解请求实体的内容类型(因此415 Unsupported Media Type状态代码不合适),并且请求实体的语法是正确的(因此400 Bad Request状态代码不合适)但无法处理包含说明。


20
投票

可能在游戏后期,但我在尝试制作REST API时偶然发现了这个语义问题。

为了扩展一点Wrikken的答案,我认为你可以使用409 Conflict403 Forbidden,具体取决于具体情况 - 简而言之,当用户无法解决冲突并完成请求时,使用403错误(例如,他们不能发送DELETE请求以显式删除资源),或者如果可能的话可以使用409。

10.4.4 403 Forbidden

服务器理解请求,但拒绝履行请求。授权无效,请求不应重复。如果请求方法不是HEAD并且服务器希望公开为什么请求没有得到满足,那么它应该描述实体中拒绝的原因。如果服务器不希望将此信息提供给客户端,则可以使用状态代码404(未找到)。

如今,有人说“403”并且出现了权限或身份验证问题,但规范说它基本上是服务器告诉客户端它不会这样做,不要再问它,这就是为什么客户端不应该“T。

至于PUTPOST ...当用户无法或不应该为资源创建标识符时,应使用POST创建资源的新实例。当资源的身份已知时使用PUT

9.6 PUT

...

POST和PUT请求之间的根本区别体现在Request-URI的不同含义上。 POST请求中的URI标识将处理所包含实体的资源。该资源可能是数据接受过程,某些其他协议的网关或接受注释的单独实体。相反,PUT请求中的URI标识请求附带的实体 - 用户代理知道URI的用途,并且服务器不得尝试将请求应用于其他资源。如果服务器希望将请求应用于不同的URI,

必须发送301(永久移动)响应;然后,用户代理可以自己决定是否重定向请求。


15
投票

返回418怎么样?

因为客户端要求保留已经存在于服务器上的实体,服务器最终会生气并认为他是茶壶并返回:418 I'm a teapot

参考文献:


13
投票

“302 Found”听起来合情合理。并且RFC 2616说除了GET和HEAD之外其他请求可以回答(这肯定包括POST)

但是它仍然让访问者通过这个URL来获取RFC的“Found”资源。为了使它直接进入真正的“找到”URL,应该使用“303 See Other”,这是有道理的,但强制另一个调用GET以下的URL。好的方面,这个GET是可缓存的。

我想我会用“303见其他”。我不知道我是否可以回复身体中发现的“东西”,但我想这样做是为了将一次往返保存到服务器上。

更新:重新阅读RFC之后,我仍然认为不存在的“4XX + 303 Found”代码应该是正确的。但是,“409 Conflict”是现有的最佳答案代码(由@ Wrikken指出),可能包括指向现有资源的Location头。


9
投票

我认为你不应该这样做。

如您所知,POST是修改集合的,它用于创建新项目。所以,如果你发送id(我认为这不是一个好主意),你应该修改集合,即修改项目,但这是令人困惑的。

用它来添加一个没有id的项目。这是最好的做法。

如果要捕获UNIQUE约束(而不是id),则可以响应409,就像在PUT请求中一样。但不是身份证。


6
投票

我认为对于REST,你只需要对该特定系统的行为做出决定,在这种情况下,我认为“正确”的答案将是这里给出的几个答案之一。如果您希望请求停止并且行为就好像客户端在继续之前犯了一个需要修复的错误,那么使用409.如果冲突真的不那么重要并且想要保持请求继续,那么通过重定向来响应客户端到找到的实体。我认为正确的REST API应该在POST之后重定向(或者至少提供位置头)到该资源的GET端点,所以这种行为将提供一致的体验。

编辑:值得注意的是,您应该考虑PUT,因为您提供了ID。然后行为很简单:“我不在乎现在有什么,把这个东西放在那里。”意思是,如果没有任何东西,它就会被创造出来;如果有东西它会被替换。我认为当服务器管理该ID时,POST更合适。分离这两个概念基本上告诉你如何处理它(即PUT是幂等的,所以只要有效载荷有效,它总是有效,POST总是创建,所以如果有ID冲突,那么409会描述这个冲突) 。


5
投票

为什么不接受202?这是一个OK请求(200s),本身没有客户端错误(400s)。

来自10 Status Code Definitions

“202已接受。该请求已被接受处理,但处理尚未完成。”

...因为它不需要完成,因为它已经存在。客户不知道它已经存在,他们没有做错任何事。

我倾向于投掷一个202,并返回类似的内容到GET /{resource}/{id}将返回的内容。

© www.soinside.com 2019 - 2024. All rights reserved.