在阅读了本文和其他数年关于状态码使用的讨论之后,我得出的主要结论是必须仔细阅读规范,重点关注所使用的术语、它们的定义、关系以及周围的环境。
从不同的答案中可以看出,经常发生的情况是,规范的某些部分脱离了上下文,并根据感觉和假设进行了孤立的解释。
这将是一个很长的答案,简短的总结是 HTTP 409 是报告“添加新资源”操作失败的最合适的状态代码,以防资源具有相同的标识符已经存在。以下是原因的解释,仅基于权威来源 - RFC 7231 中所述的内容。
那么为什么409 Conflict 在 OP 的问题中描述的情况下是最合适的状态代码?
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状态码描述中提到。