当资源已经存在时,POST请求的HTTP响应代码是什么?

1309

我正在构建一个服务器,允许客户端存储对象。这些对象在客户端完全构造,包括对象ID在内,而这些ID对于对象的整个生命周期都是固定的。

我已经定义了API,使得客户端可以使用PUT创建或修改对象:

PUT /objects/{id} HTTP/1.1
...

{json representation of the object}

{id}是对象的ID,因此它是请求URI的一部分。

现在,我还考虑允许客户端使用POST创建该对象:

POST /objects/ HTTP/1.1
...

{json representation of the object, including ID}

由于POST是“追加”操作,如果对象已经存在,我不确定该怎么办。 我应该将请求视为修改请求还是返回某些错误代码(哪个)?


7
截至2016年6月,如果电子邮件已存在,FB在注册时明确设置为200。 - Green
14
使用已存在的名称创建资源(团队/仓库),Github API 返回422错误。 - Ken
2
这取决于您是否认为对象的存在是错误。如果您处理附加操作,那么200或204是最合适的响应代码。 - Suncat2000
2
总的说来,这是一个409冲突和422无法处理实体之间的抉择 - 我认为这里的答案权重指向409,从人类的角度来看更容易理解。 - danday74
@Green 这将防止账户枚举攻击。 - Cameron Martin
1
我用409来表示正常情况,422则表示不良表单。 - Anthony O
18个回答

7
我认为对于REST,你只需要为该特定系统做出行为决策,在这种情况下,我认为“正确”的答案将是以下几个答案之一。如果您希望请求停止并表现得好像客户端犯了一个错误需要修复才能继续,则使用409。如果冲突并不重要并且想要继续请求,则通过将客户端重定向到找到的实体来响应。我认为适当的REST API应该在POST后重定向(或至少提供位置标头)到该资源的GET端点,因此此行为将提供一致的体验。
编辑: 值得注意的是,由于提供了ID,因此应考虑使用PUT。然后行为很简单:“我不关心现在有什么,把这个东西放在那里。”这意味着,如果没有什么东西,它将被创建;如果有什么东西,它将被替换。我认为当服务器管理该ID时,POST更加合适。分离这两个概念基本上告诉您如何处理它(即PUT是幂等的,因此只要负载验证正确,它就应该始终起作用,POST总是创建,因此如果发生ID冲突,则使用409来描述该冲突)。

根据规范,暗示着一个正确使用的 POST 请求不可能返回错误 409,因为规范指出它应该在与目标资源冲突时返回。由于目标资源尚未发布,因此不可能发生冲突,因此回复 409 Conflict 没有任何意义。 - Grant Gryczan
1
在我看来,这是有争议的。如果您发布到/users,则资源是集合,而不是单个记录/users/{id}。 - Sinaesthetic
我不再同意我之前的评论了。我改变主意的原因可以在我的回答底部看到解释。 - Grant Gryczan

3
另一个潜在的治疗方法是使用 PATCH。PATCH 被定义为改变内部状态的东西,不限于追加。通过允许您更新已经存在的项目,PATCH 可以解决这个问题。请参见:RFC 5789: PATCH

2
Patch就像PUT一样,但不是完全替换。它用于修改资源的一部分,例如添加、删除或修改资源的单个元素,而不是整体替换。 - Sinaesthetic
他们不想更新现有的项目。他们想创建一个新项目,但存在名称冲突。 - Grant Gryczan

2
208 - http://httpstatusdogs.com/208-already-reported怎么样?这是一个选项吗?
依我看,如果唯一的问题是重复的资源,就不应该引发任何错误。毕竟,客户端和服务器端都没有出现任何错误。

1
由于您想要添加的某个项目已经存在,因此这不是一个选项。所以您尝试添加一些内容,但实际上已经存在了。只有在数据集增长时才能应用 A OK。追加某物 -> 好的,我没有追加任何东西。我猜不适合。 - Martin Kersten
就像我所说的,我认为这不是一个错误。但我理解@martin的观点。 - Fernando Ferreira
如果资源未成功创建,则根据定义存在错误。 - Grant Gryczan
POST也用于追加数据。这是根据定义,不是错误。 - Suncat2000
@Suncat2000 即使是这种情况,如果数据没有成功追加,仍然会出现错误。而且如果资源已经存在,就不会追加任何数据。 - Grant Gryczan
DAV绑定的成员已经在先前的回复中枚举过了,不会再次包含在内。 - Fernando Ferreira

2
更可能的情况是 400 Bad Request 【6.5.1. 400 Bad Request】
400(错误请求)状态码表示服务器由于认为存在客户端错误(例如,格式错误的请求语法、无效的请求消息框架或欺骗性请求路由等)而无法或不会处理该请求。
由于请求包含重复值(已经存在的值),因此可以被视为客户端错误。在下一次尝试之前需要更改请求。
考虑到这些事实,我们可以得出结论,HTTP 状态码为 400 Bad Request。

1
错误请求意味着数据包的语法存在固有问题。如果在另一个上下文中(例如资源尚不存在),数据包将成功,则不应返回错误400。 - Grant Gryczan

2

在检查重复记录的正确代码时,我偶然发现了这个问题。

请原谅我的无知,但我不明白为什么每个人都忽略了代码“300”,它明确表示“多项选择”或“模棱两可”。

在我看来,这将是用于构建非标准或特定系统以供自己使用的完美代码。当然,我也可能是错的!

https://www.rfc-editor.org/rfc/rfc7231#section-6.4.1


我的理解是:“状态码表示目标资源具有多个表示形式...提供有关这些标识符的信息,以便用户(或用户代理)可以通过将其请求重定向到其中一个或多个标识符来选择首选表示形式”。我们明确地试图防止出现多个表示形式。没有选择。客户端没有可供选择的替代方案。客户端应该使用不同的ID重新提交。话虽如此,人们也应该考虑在客户端与服务器中生成唯一的ID。 - musicin3d
1
从语义上讲,客户端的意思是“创建这个”,而服务器的响应是“去这里”。这段对话没有任何意义。就好像服务器在告诉客户端“改为发布到这个位置”。在服务器响应“好的,我已经创建了它并放在这里”时,300s更适合作为GET请求或POST请求的响应。 - Sinaesthetic

2

因为您提到使用post创建对象的请求包含对象的ID,所以您应该将其作为幂等请求。只需返回与成功创建请求完全相同的响应即可。幂等请求使API更简单,例如现在客户端无需担心两种不同情况(成功、失败),或者客户端可以安全地重试请求,以防连接/服务器暂时出现故障。


有时候这是有意义的。但是有时候你想要出现错误。例如,如果用户尝试使用已经存在的电子邮件地址创建帐户,则应该显示错误并指示其登录。 - Charlie Martin

1

错误402,需要付款

也就是说,这个资源已经存在,但如果你给我足够的钱,我会删除当前的资源并把它给你:D

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

作为一个更严肃的回答,这里没有人提供,那么451:由于法律原因不可用。您不能“合法地(根据自己制定的条款和条件)”让多个人访问同一帐户信息。

422也是一个很好的选择,即无法处理的实体。请求格式良好,但由于与另一个条目在语义上相等,因此无法跟随。


哈哈哈,我喜欢这个! - Ben Rauzi
451状态码不是用于GET请求的吗?因为规范说明了“...表明用户请求的资源由于法律原因不可用,例如针对某个网页已经发起了法律行动”。 - orimdominic
这很有趣,但是除非你只是为了开玩笑而不是为公共生产应用程序制作REST API,请不要实际使用错误码402或451。任何一个都会极其令人困惑。这与付款或法律条款无关。 - Grant Gryczan

-10

7
403是禁止访问的意思。 - selalerer
你是对的 @selalerer,客户端被禁止执行该操作(添加相同的资源)。 - Manjunath Bhadrannavar
7
这不是一个权威的错误代码定义来源,这是他们的API代码的代码列表及其定义。 - mxcl
为什么Rebrandly选择修改HTTP响应代码规范而不是在末尾添加新代码,这很奇怪。@ManjunathBhadrannavar可能已经在过去两年中学到了,但401未经授权意味着用户未经身份验证(缺少或错误的凭据),而403禁止意味着未经授权(凭证正确,但缺少该端点的权限)。 403在这里不是正确的选择,因为它与客户端缺少权限无关。 - Patrick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接