在阅读了这篇文章和其他几篇关于状态码使用的长达数年的讨论之后,我得出的主要结论是,必须仔细阅读规范,重点关注正在使用的术语、它们的定义、关系和周围的上下文。
相反,从不同的答案中可以看出,经常发生的情况是,部分规范被剥离了上下文,并基于感觉和假设进行孤立的解释。
这将是一个相当长的回答,简短的总结是,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冲突状态代码描述中提到的与目标资源当前状态的冲突。