我正在构建一个允许客户端存储对象的服务器。这些对象是在客户端完全构造的,对象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 409是报告“添加新资源”操作失败的最合适的状态代码,如果具有相同标识符的资源已经存在。以下是对原因的解释,完全基于权威来源- RFC 7231中所述。

那么,在OP的问题中描述的情况下,为什么409冲突是最合适的状态代码?

RFC 7231对409冲突状态码的描述如下:

409(冲突)状态代码表示由于与目标资源的当前状态冲突而无法完成请求。

这里的关键组件是目标资源及其状态。

目标资源

RFC 7231对该资源的定义如下:

HTTP请求的目标称为“资源”。HTTP不限制资源的性质;它仅仅定义了一个可能用于与资源交互的接口。每个资源由统一资源标识符(Uniform resource Identifier, URI)标识,如[RFC7230]章节2.7所述。

因此,当使用HTTP接口时,我们总是通过对uri应用HTTP方法来操作由uri标识的资源。

当我们打算添加一个新资源时,基于OP的例子,我们可以:

在资源/objects/{id}中使用PUT; 对资源/对象使用POST。

/objects/{id}无关紧要,因为在使用PUT方法时不可能有冲突:

PUT方法请求用请求消息有效负载中包含的表示所定义的状态创建或替换目标资源的状态。

如果具有相同标识符的资源已经存在,它将被PUT替换。

我们将关注/objects资源和POST。

RFC 7231对POST说:

POST方法要求目标资源根据资源自己的特定语义处理请求中包含的表示。例如,POST用于以下函数(以及其他函数):…3)创建一个尚未被源服务器识别的新资源;4)将数据追加到资源的现有表示中。

与OP对POST方法的理解相反:

由于POST的意思是“追加”操作…

将数据追加到资源的现有表示只是可能的POST“函数”之一。此外,OP在提供的例子中实际上所做的不是直接将数据追加到/objects表示中,而是创建一个新的独立资源/objects/{id},然后它成为/objects表示的一部分。但这并不重要。

重要的是资源表示的概念,它把我们带到了……

资源状态

RFC 7231解释说:

考虑到资源可以是任何东西,并且HTTP提供的统一接口类似于一个窗口,通过该窗口,人们只能通过将消息传递给另一端的某个独立参与者来观察并对该事物采取行动,因此需要一个抽象来表示(“代替”)我们通信中该事物的当前或期望状态。这种抽象称为表示[REST]。

对于HTTP的目的,“表示”是旨在反映给定资源的过去、当前或期望状态的信息,其格式可以通过协议轻松通信,并由一组表示元数据和潜在的无限表示数据流组成。

这还不是全部,规范继续描述表示部分——元数据和数据,但是我们可以总结出,由元数据(报头)和数据(有效负载)组成的资源表示反映了资源的状态。

现在,我们了解了409冲突状态代码的用法所需的两个部分。

409年冲突

让我们重申:

409(冲突)状态代码表示由于与目标资源的当前状态冲突而无法完成请求。

那么它是如何合适的呢?

We POST to /objects => our target resource is /objects. OP does not describe the /objects resource, but the example looks like a common scenario where /objects is a resource collection, containing all individual "object" resources. That is, the state of the /objects resource includes the knowledge about all existing /object/{id} resources. When the /objects resource processes a POST request it has to a) create a new /object/{id} resource from the data passed in the request payload; b) modify its own state by adding the data about the newly created resource. When a resource to be created has a duplicate identifier, that is a resource with the same /object/{id} URI already exists, the /objects resource will fail to process the POST request, because its state already includes the duplicate /object/{id} URI in it.

这正是409冲突状态代码描述中提到的与目标资源当前状态的冲突。

其他回答

更有可能是400个坏请求

[* * 6.5.1。400错误请求**][1]

400(坏请求)状态代码表示服务器不能或 将不会处理请求由于某些东西被认为是 客户端错误(例如,格式错误的请求语法,无效的请求 消息框架,或欺骗性请求路由)。

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

在您的情况下,您可以使用409冲突

如果你想检查另一个HTTPs状态码从下面的列表

1××信息

100 Continue
101 Switching Protocols
102 Processing

2××成功

200 OK
201 Created
202 Accepted
203 Non-authoritative Information
204 No Content
205 Reset Content
206 Partial Content
207 Multi-Status
208 Already Reported
226 IM Used

3××重定向

300 Multiple Choices
301 Moved Permanently
302 Found
303 See Other
304 Not Modified
305 Use Proxy
307 Temporary Redirect
308 Permanent Redirect

4××客户端错误

400 Bad Request
401 Unauthorized
402 Payment Required
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable
407 Proxy Authentication Required
408 Request Timeout
409 Conflict
410 Gone
411 Length Required
412 Precondition Failed
413 Payload Too Large
414 Request-URI Too Long
415 Unsupported Media Type
416 Requested Range Not Satisfiable
417 Expectation Failed
418 I’m a teapot
421 Misdirected Request
422 Unprocessable Entity
423 Locked
424 Failed Dependency
426 Upgrade Required
428 Precondition Required
429 Too Many Requests
431 Request Header Fields Too Large
444 Connection Closed Without Response
451 Unavailable For Legal Reasons
499 Client Closed Request

5××服务器错误

500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
505 HTTP Version Not Supported
506 Variant Also Negotiates
507 Insufficient Storage
508 Loop Detected
510 Not Extended
511 Network Authentication Required
599 Network Connect Timeout Error

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

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

It seems that 409 Conflict is the most common answer here, but, according to the spec, that implies that the resource already exists and the new data you are applying to it is incompatible with its current state. If you are sending a POST request, with, for example, a username that is already taken, it's not actually conflicting with the target resource, as the target resource (the resource you're trying to create) has not yet been posted. It's an error specifically for version control, when there is a conflict between the version of the resource stored and the version of the resource requested. It's very useful for that purpose, for example when the client has cached an old version of the resource and sends a request based on that incorrect version which would no longer be conditionally valid. "In this case, the response representation would likely contain information useful for merging the differences based on the revision history." The request to create another user with that username is just unprocessable, having nothing to do with any version conflict.

为了记录,422也是GitHub使用的状态码,当您尝试按已使用的名称创建存储库时。

也许是在游戏后期,但我在尝试创建REST API时偶然发现了这个语义问题。

为了稍微扩展一下wrkken的回答,我认为您可以根据情况使用409 Conflict或403 Forbidden -简而言之,当用户完全无法解决冲突并完成请求时使用403错误(例如,他们不能发送DELETE请求来显式删除资源),或者如果可能做一些事情,则使用409。

10.4.4禁止403 服务器理解请求,但拒绝执行。 授权没有帮助,请求不应该重复。如果 请求方法不是HEAD,服务器希望公开 为什么要求没有被满足,它应该描述原因 对于实体中的拒绝。如果服务器不希望做出 此信息可用于客户端,状态代码404(不是 Found)可以代替。

现在,有人说“403”,就会想到权限或身份验证问题,但规范说,这基本上是服务器告诉客户端它不会这样做,不要再问了,这就是客户端不应该这样做的原因。

至于PUT vs. POST…当用户没有办法或不应该为资源创建标识符时,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.

错误402,需要付款

例如,这个资源已经存在,但如果你给我足够的钱,我会删除当前的一个,给你:D

...但是看看mozilla在https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses上对状态码的定义

作为一个更严肃的答案,这里没有人提供,那451呢:由于法律原因无法使用。你不能“合法地(通过你自己制定的条款和条件)”让多人访问相同的帐户信息

422也是一个很好的选择,它是不可处理的实体 请求格式良好,但由于语义错误而无法执行。由于它是一个完全有效的请求,但由于它在语义上与另一个条目相等,所以不能遵循它。