关于rest:资源已存在时POST的HTTP响应代码

HTTP response code for POST when resource already exists

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

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

1
2
3
4
PUT /objects/{id} HTTP/1.1
...

{json representation of the object}

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

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

1
2
3
4
POST /objects/ HTTP/1.1
...

{json representation of the object, including ID}

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


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

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.

Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.


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


我个人使用WebDAV扩展422 Unprocessable Entity

根据RFC 4918

The 422 Unprocessable Entity status code means the server understands the content type of the request entity (hence a 415 Unsupported Media Type status code is inappropriate), and the syntax of the request entity is correct (thus a 400 Bad Request status code is inappropriate) but was unable to process the contained instructions.


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

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

10.4.4 403 Forbidden

The server understood the request, but is refusing to fulfill it.
Authorization will not help and the request SHOULD NOT be repeated. If
the request method was not HEAD and the server wishes to make public
why the request has not been fulfilled, it SHOULD describe the reason
for the refusal in the entity. If the server does not wish to make
this information available to the client, the status code 404 (Not
Found) can be used instead.

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

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

9.6 PUT

...

The fundamental difference between the POST and PUT requests is
reflected in the different meaning of the Request-URI. The URI in a
POST request identifies the resource that will handle the enclosed
entity. That resource might be a data-accepting process, a gateway to
some other protocol, or a separate entity that accepts annotations. In
contrast, the URI in a PUT request identifies the entity enclosed with
the request -- the user agent knows what URI is intended and the
server MUST NOT attempt to apply the request to some other resource.
If the server desires that the request be applied to a different URI,

it MUST send a 301 (Moved Permanently) response; the user agent MAY
then make its own decision regarding whether or not to redirect the
request.


"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头。


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

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

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

要隐式处理重复项,请查看2XX:

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

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

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

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

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


我认为你不应该这样做。

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

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

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


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

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


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

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

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

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


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

来自10个状态码定义:

"202 Accepted. The request has been accepted for processing, but the processing has not been completed."

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

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


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

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


更可能是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错误请求。


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

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

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

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


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

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