我正在构建一个允许客户端存储对象的服务器。这些对象完全在客户端构建,并带有在对象的整个生命周期内永久存在的对象 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 的意思是“追加”操作,我不确定如果对象已经存在该怎么办。我应该将请求视为修改请求还是应该返回一些错误代码(哪个)?
我的感觉是 409 Conflict
是最合适的,但是,当然很少在野外看到:
由于与资源的当前状态冲突,无法完成请求。仅在预期用户可能能够解决冲突并重新提交请求的情况下才允许使用此代码。响应正文应该包含足够的信息让用户识别冲突的来源。理想情况下,响应实体将包含足够的信息供用户或用户代理解决问题;但是,这可能是不可能的,也不是必需的。响应 PUT 请求时最有可能发生冲突。例如,如果正在使用版本控制并且被 PUT 的实体包括对资源的更改,这些更改与早期(第三方)请求所做的更改相冲突,则服务器可能会使用 409 响应来指示它无法完成请求.在这种情况下,响应实体可能会以响应 Content-Type 定义的格式包含两个版本之间差异的列表。
根据 RFC 7231,可以使用 303 See Other如果处理 POST 的结果等同于现有资源的表示。
这一切都与上下文有关,以及谁负责处理请求的重复(服务器或客户端或两者)
如果服务器只是指向副本,请查看 4xx:
400 Bad Request - 当服务器因为明显的客户端故障而不会处理请求时
409 Conflict - 如果服务器不处理请求,但原因不是客户端的错
...
对于重复的隐式处理,请查看 2XX:
200 好
201 已创建
...
如果预计服务器会返回一些东西,请查看 3XX:
302 找到
303 查看其他
...
当服务器能够指向现有资源时,它意味着重定向。
如果以上内容还不够,最好在响应正文中准备一些错误消息。
我个人使用 WebDAV 扩展程序 422 Unprocessable Entity
。
422 Unprocessable Entity 状态码表示服务器理解请求实体的内容类型(因此 415 Unsupported Media Type 状态码是不合适的),并且请求实体的语法是正确的(因此 400 Bad Request 状态码是不合适的)但无法处理包含的指令。
422
让我觉得很奇怪......
也许游戏迟到了,但我在尝试制作 REST API 时偶然发现了这个语义问题。
为了扩展 Wrikken 的答案,我认为您可以根据情况使用 409 Conflict
或 403 Forbidden
- 简而言之,当用户完全无法解决冲突并完成请求时使用 403 错误(例如他们不能发送 DELETE
请求来显式删除资源),或者如果可以完成某些事情,则使用 409。
10.4.4 403 Forbidden 服务器理解请求,但拒绝执行。授权将无济于事,并且不应重复请求。如果请求方法不是 HEAD 并且服务器希望公开请求未完成的原因,它应该在实体中描述拒绝的原因。如果服务器不希望向客户端提供此信息,则可以使用状态代码 404(未找到)来代替。
现在,有人说“403”并且想到了权限或身份验证问题,但规范说它基本上是服务器告诉客户端它不会这样做,不要再问它,这就是为什么客户端应该不。
至于 PUT
与 POST
... POST
应该用于在用户无法或不应该为资源创建标识符时创建资源的新实例。当资源的身份已知时使用 PUT
。
9.6 PUT ... POST和PUT请求的根本区别在于Request-URI的不同含义。 POST 请求中的 URI 标识将处理封闭实体的资源。该资源可能是一个数据接受进程,一个通往其他协议的网关,或者一个接受注释的单独实体。相比之下,PUT 请求中的 URI 标识了请求中包含的实体——用户代理知道 URI 的意图,服务器不得尝试将请求应用于其他资源。如果服务器希望将请求应用于不同的 URI,它必须发送 301(永久移动)响应;然后用户代理可以自己决定是否重定向请求。
POST
请求返回(正确使用时),因为它指出当它与 目标资源 冲突时应该返回它。由于目标资源还没有发布到,它不可能发生冲突,因此用 409 Conflict
回复没有任何意义。
POST
不能返回 409 错误,事实上,我会推断相反,因为“在响应 PUT 请求时最有可能发生冲突。”似乎表明其他请求方法也可以使用此代码。此外,“响应主体应该包含足够的信息,以便用户识别冲突的来源。理想情况下,响应实体将包含足够的信息,以便用户或用户代理解决问题;但是,这可能是不可能的,并且不需要。” (webdav.org/specs/rfc2616.html#status.409)
PATCH
,而不是 POST
。有意义的是,PATCH
不太可能发生冲突,因为 PUT
请求必须包含有关资源的所有信息,而 PATCH
只需声明更改。另一方面,与 PUT
一样,POST
也必须包含有关资源的所有信息,因此如果它们暗示 POST
请求不太可能发生冲突,那就没有意义了。事实上,考虑到我上次评论的推理,在 POST
请求中存在冲突甚至没有意义。
我会选择 422 Unprocessable Entity
,它在请求无效但问题不在语法或身份验证时使用。
作为反对其他答案的论据,使用任何非 4xx
错误代码都意味着它不是客户端错误,而且显然是。使用非 4xx
错误代码来表示客户端错误完全没有意义。
似乎 409 Conflict
是这里最常见的答案,但是根据规范,这意味着资源已经存在并且您应用到它的新数据与其当前状态不兼容。如果您发送 POST
请求,例如,用户名已被占用,它实际上并没有与目标资源冲突,因为目标资源(您尝试创建的资源)尚未发布.当存储的资源版本与请求的资源版本之间存在冲突时,这是专门针对版本控制的错误。它对此非常有用,例如,当客户端缓存了资源的旧版本并基于不再有条件有效的不正确版本发送请求时。 “在这种情况下,响应表示可能包含有助于根据修订历史合并差异的信息。”使用该用户名创建另一个用户的请求是无法处理的,与任何版本冲突无关。
作为记录,422 也是 GitHub 在您尝试使用已在使用的名称创建存储库时使用的状态代码。
在阅读了这篇文章和其他几年关于状态码使用的讨论之后,我得出的主要结论是必须仔细阅读规范,重点关注所使用的术语、它们的定义、关系和周围的上下文.
从不同的答案中可以看出,通常会发生的情况是,规范的某些部分脱离了上下文,并根据感觉和假设进行了孤立的解释。
这将是一个很长的答案,简短的总结是 HTTP 409 是报告“添加新资源”操作失败的最合适的状态代码,以防已经具有相同标识符的资源存在。以下是仅基于权威来源 - RFC 7231 中所述内容的解释。
那么在 OP 的问题中描述的情况下,为什么 409 Conflict
是最合适的状态代码?
RFC 7231 描述 409 Conflict
状态代码如下:
409(冲突)状态码表示由于与目标资源的当前状态冲突,请求无法完成。
这里的关键组件是目标资源及其状态。
目标资源
资源由 RFC 7231 定义如下:
HTTP 请求的目标称为“资源”。 HTTP 不限制资源的性质;它仅仅定义了一个可以用来与资源交互的接口。每个资源都由统一资源标识符 (URI) 标识,如 [RFC7230] 的第 2.7 节所述。
因此,当使用 HTTP 接口时,我们总是通过对 URI 标识的资源应用 HTTP 方法来操作它们。
当我们的意图是添加新资源时,基于 OP 的示例,我们可以:
将 PUT 与资源 /objects/{id} 一起使用;
将 POST 与资源 /objects 一起使用。
/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 Conflict
状态代码的用法所需的两个部分。
409 冲突
让我们重申:
409(冲突)状态码表示由于与目标资源的当前状态冲突,请求无法完成。
那么如何搭配呢?
我们 POST 到 /objects => 我们的目标资源是 /objects。 OP 没有描述 /objects 资源,但该示例看起来像是一个常见场景,其中 /objects 是一个资源集合,包含所有单独的“对象”资源。也就是说,/objects 资源的状态包括所有现有 /object/{id} 资源的知识。当 /objects 资源处理 POST 请求时,它必须 a) 从请求有效负载中传递的数据创建一个新的 /object/{id} 资源; b) 通过添加有关新创建资源的数据来修改自己的状态。当要创建的资源有重复标识符,即已经存在具有相同 /object/{id} URI 的资源时,/objects 资源将无法处理 POST 请求,因为它的状态已经包含重复的 /object/ {id} URI。
这正是与目标资源当前状态的冲突,在 409 Conflict
状态码说明中有所提及。
在您的情况下,您可以使用 409 Conflict
如果您想从下面的列表中检查另一个 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
我认为你不应该这样做。
如您所知,POST 用于修改集合,它用于创建新项目。所以,如果你发送id(我认为这不是一个好主意),你应该修改collection,即修改item,但它是混乱的。
用它来添加一个项目,没有 id。这是最好的做法。
如果你想捕获一个 UNIQUE 约束(不是 id),你可以响应 409,就像在 PUT 请求中一样。但不是身份证。
“302 Found”对我来说听起来很合乎逻辑。 RFC 2616 表示它可以回答除 GET 和 HEAD 之外的其他请求(这肯定包括 POST)
但它仍然让访问者通过 RFC 访问此 URL 以获取此“找到”资源。要使其直接转到真正的“找到”URL,应该使用“303 See Other”,这是有道理的,但会强制另一个调用 GET 其以下 URL。从好的方面来说,这个 GET 是可缓存的。
我想我会使用“303 See Other”。我不知道我是否可以用在正文中找到的“东西”来响应,但我想这样做以节省到服务器的一次往返。
更新:重新阅读 RFC 后,我仍然认为不存在的“4XX+303 Found”代码应该是正确的。但是,“409 冲突”是现有的最佳答案代码(@Wrikken 指出),可能包括指向现有资源的 Location 标头。
我认为对于 REST,您只需要对该特定系统的行为做出决定,在这种情况下,我认为“正确”的答案将是这里给出的几个答案之一。如果您希望请求停止并表现得好像客户端犯了一个需要在继续之前修复的错误,则使用 409。如果冲突确实不那么重要并且希望继续请求,则通过重定向来响应客户到找到的实体。我认为适当的 REST API 应该在 POST 之后重定向(或至少提供位置标头)到该资源的 GET 端点,因此这种行为将提供一致的体验。
编辑:还值得注意的是,由于您提供了 ID,因此您应该考虑使用 PUT。然后行为很简单:“我不管现在有什么,把这个东西放在那里。”意思是,如果什么都没有,它将被创建;如果有东西,它将被替换。我认为当服务器管理该 ID 时,POST 更合适。分离这两个概念基本上告诉您如何处理它(即 PUT 是幂等的,因此只要有效负载验证它就应该始终工作,POST 总是创建,因此如果 ID 发生冲突,那么 409 将描述该冲突) .
POST
请求返回(正确使用时),因为它指出当它与 目标资源 冲突时应该返回它。由于目标资源还没有发布到,它不可能发生冲突,因此用 409 Conflict
回复没有任何意义。
PUT
的建议。
毕竟,另一种潜在的治疗方法是使用 PATCH。 PATCH 被定义为改变内部状态的东西,并且不限于附加。
PATCH 将通过允许您更新现有项目来解决问题。请参阅:RFC 5789: PATCH
那么 208 - http://httpstatusdogs.com/208-already-reported 呢?这是一个选择吗?
在我看来,如果唯一的事情是重复资源,则不应引发错误。毕竟,客户端或服务器端都没有错误。
在检查重复记录的正确代码时偶然发现了这个问题。
请原谅我的无知,但我不明白为什么每个人都忽略代码“300”,它清楚地表示“多项选择”或“模棱两可”
在我看来,这将是构建非标准或特定系统供您自己使用的完美代码。我也可能是错的!
https://www.rfc-editor.org/rfc/rfc7231#section-6.4.1
更有可能是400 Bad Request
[**6.5.1。 400 错误请求**][1]
400 (Bad Request) 状态码表示服务器不能或不会处理请求,因为某些东西被认为是客户端错误(例如,格式错误的请求语法、无效的请求消息帧或欺骗性请求路由)。
由于请求包含重复值(已存在的值),因此可以将其视为客户端错误。需要在下次尝试之前更改请求。通过考虑这些事实,我们可以得出 HTTP STATUS 400 Bad Request 的结论。
402错误,需要付款
IE 这个资源已经存在,但如果你给我足够的钱,我会删除当前资源并给你:D
...但是在 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses 处查看 Mozilla 对状态代码的定义
作为这里没有人提供的更严肃的答案,451 怎么样:由于法律原因不可用。您不能“合法地(根据您自己制定的条款和条件)”让多个人访问同一帐户信息
422 也是一个不错的选择,它是 Unprocessable Entity 请求格式正确,但由于语义错误而无法遵循。因为它是一个完全有效的请求,但由于它在语义上等于另一个条目,所以它不能被遵循。
由于您提到使用 post 的对象创建请求包含对象的 ID,因此您应该将其设为幂等请求。只需返回与成功创建请求完全相同的响应。幂等请求使 API 更简单,例如。现在客户不必担心两种不同的情况(成功、失败)。或者客户端可以安全地重试请求,以防连接/服务器暂时关闭出现问题。
这是用户侧故障,属于 4xx 组。这是正确的答案https://developers.rebrandly.com/docs/403-already-exists-errors
HTTP 409
,并带有一个指向现有/冲突资源的Location
标头。