我正在构建一个允许客户端存储对象的服务器。这些对象是在客户端完全构造的,对象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意味着“追加”操作,我不确定在对象已经存在的情况下该做什么。我应该把请求作为修改请求,还是应该返回一些错误代码(哪个)?


当前回答

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


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

400坏请求——服务器不会处理一个请求,因为这是明显的客户端错误 409冲突——如果服务器不处理请求,但原因不是客户端的错 ...

对于副本的隐式处理,请参阅2XX:

200好了 201年创建 ...

如果服务器希望返回一些东西,请查看3XX:

302年发现的 303查看其他 ...

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


如果以上还不够,那么在响应体中准备一些错误消息总是一个很好的做法。

其他回答

更有可能是400个坏请求

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

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

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

我觉得你不应该这么做。

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

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

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

“302 Found”对我来说听起来合乎逻辑。RFC 2616说除了GET和HEAD之外,它还可以被其他请求回答(当然也包括POST)

但是它仍然让访问者通过RFC访问这个URL来获得这个“已找到”的资源。为了使它直接指向真正的“Found”URL,应该使用“303 See Other”,这是有意义的,但强制另一个调用GET它后面的URL。好的方面是,这个GET是可缓存的。

我想我会用“303 See Other”。我不知道我是否可以响应在主体中发现的“东西”,但我想这样做,以节省到服务器的一次往返。

更新:在重新阅读RFC后,我仍然认为不存在的“4XX+303 Found”代码应该是正确的。然而,“409冲突”是现有的最好的答案代码(正如@ wrkken指出的那样),可能包括指向现有资源的Location头。

在阅读了这篇文章和其他几篇关于状态码使用的长达数年的讨论之后,我得出的主要结论是,必须仔细阅读规范,重点关注正在使用的术语、它们的定义、关系和周围的上下文。

相反,从不同的答案中可以看出,经常发生的情况是,部分规范被剥离了上下文,并基于感觉和假设进行孤立的解释。

这将是一个相当长的回答,简短的总结是,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冲突状态代码描述中提到的与目标资源当前状态的冲突。

就我个人而言,我倾向于WebDAV扩展422不可处理实体。

根据RFC 4918

422不可处理实体状态码意味着服务器理解请求实体的内容类型(因此415不支持的媒体类型状态码是不合适的),并且请求实体的语法是正确的(因此400坏请求状态码是不合适的),但无法处理包含的指令。