当资源已经存在时,用于POST的HTTP响应代码
我正在构build一个允许客户端存储对象的服务器。 这些对象是在客户端完全构build的,完整的对象ID在对象的整个生命周期中是永久的。
我定义了API,以便客户可以使用PUT创build或修改对象:
PUT /objects/{id} HTTP/1.1 ... {json representation of the object}
{id}是对象ID,所以它是Request-URI的一部分。
现在,我还考虑允许客户使用POST创build对象:
POST /objects/ HTTP/1.1 ... {json representation of the object, including ID}
由于POST的意思是“追加”操作,我不知道如果对象已经存在,该怎么办。 我应该将请求视为修改请求还是应该返回一些错误代码(哪个)?
我的感觉是409 Conflict
是最合适的,然而,很less在野外看到:
由于与当前资源状态的冲突,请求无法完成。 此代码仅在预期用户可能能够解决冲突并重新提交请求的情况下才被允许。 响应主体应该包含足够的信息让用户识别冲突的来源。 理想情况下,响应实体会为用户或用户代理提供足够的信息来解决问题; 然而,这可能是不可能的,也不是必需的。
冲突最有可能发生在响应PUT请求。 例如,如果正在使用版本控制,并且包含PUT的实体更改为与先前(第三方)请求相冲突的资源,则服务器可能会使用409响应来指示无法完成请求。 在这种情况下,响应实体可能会以响应Content-Type定义的格式包含两个版本之间的差异列表。
我个人用WebDAV扩展422 Unprocessable Entity
。
REST模式将其描述为
422 Unprocessable Entity
状态码意味着服务器理解请求实体的内容types(因此415 Unsupported Media Type
状态码是不合适的),并且请求实体的语法是正确的(因此400 Bad Request
状态码是不恰当的)但无法处理包含的说明。
根据RFC 7231 ,a 303可以使用如果处理POST的结果等同于现有资源的表示 。
也许迟到了,但是我试图制作一个REST API的时候偶然发现了这个语义问题。
在Wrikken的回答中,我想你可以使用409 Conflict
或者403 Forbidden
,简而言之,当用户完全没有办法解决冲突并完成请求时,可以使用403错误(例如,他们可以不发送DELETE
请求来显式删除资源),或者在可能的情况下使用409。
10.4.4 403禁止
服务器了解请求,但拒绝履行。 授权不起作用,请求不应重复。 如果请求方法不是HEAD,服务器希望公开为什么请求没有被满足,那么它应该描述拒绝的原因。 如果服务器不希望将这些信息提供给客户端,则可以使用状态码404(Not Found)。
现在,有人说“403”和一个权限或authentication的问题,但规范说,这基本上是服务器告诉客户,它不会这样做,不要再问了,这就是为什么客户端不应该“T。
至于PUT
与POST
…当用户无法或不应该为资源创build标识符时,应该使用POST
来创build新的资源实例。 当资源的身份已知时使用PUT
。
9.6 PUT
…
POST和PUT请求之间的根本区别反映在Request-URI的不同含义中。 POST请求中的URI标识将处理封闭实体的资源。 该资源可能是数据接受过程,某个其他协议的入口,也可能是接受注释的单独实体。 相比之下,PUT请求中的URI标识了请求中包含的实体 – 用户代理知道哪个URI是预期的,服务器不能尝试将请求应用到其他资源。 如果服务器希望将请求应用于不同的URI,
它必须发送301(永久移动)响应; 用户代理可以自行决定是否redirect请求。
“302 Found”对我来说听起来很合理。 和RFC 2616说,它可以回答的其他请求比GET和HEAD(这肯定包括POST)
但它仍然保持访问者去这个URL获得这个“find”资源,由RFC。 为了使其直接进入真正的“发现”URL,应该使用“303 See Other”,这是有道理的,但强制另一个电话来获得它的下面的URL。 好的一面,这个GET是可caching的。
我想我会用“303见其他” 。 我不知道我是否可以回应在身体中发现的“事情”,但我想这样做,以节省一个往返服务器。
更新:在重新阅读RFC后,我仍然认为不存在的 “4XX + 303 Found”代码应该是正确的。 然而, “409冲突”是最好的现有答案代码 (正如@ Wrikken指出的那样),可能包括指向现有资源的位置标题。
我不认为你应该这样做。
如你所知,POST是修改集合的,它用于创build一个新的项目。 所以,如果你发送id(我认为这不是一个好主意),你应该修改这个集合,也就是修改这个item,但这样做很混乱。
使用它来添加一个项目,没有ID。 这是最好的做法。
如果你想捕获一个UNIQUE约束(不是id),你可以响应409,就像你在PUT请求中所做的那样。 但不是ID。
怎么回418?
因为客户端要求坚持服务器上已经存在的实体,服务器终于发疯了,认为他是一个茶壶,并返回: 418 I'm a teapot
。
参考文献:
- RFC 2324
- 实际用法
- 博客解释代码
- 所以问题
我认为对于REST,你只需要对特定系统的行为做出决定,在这种情况下,我认为“正确的”答案将是这里给出的几个答案之一。 如果您希望请求停止,并且performance得像客户在继续之前需要修复的错误那么使用409.如果冲突确实不是那么重要,并且希望保持请求继续,那么通过redirect客户端到find的实体。 我认为适当的REST API应该redirect(或至less提供位置标头)到POST后的POST端点,所以这种行为会带来一致的体验。
编辑:还值得注意的是,你应该考虑一个PUT,因为你提供的ID。 那么这个行为很简单:“我不在乎现在有什么东西,把这个东西放在那里。” 意思是,如果什么都没有,就会被创造出来。 如果有东西在那里,它会被取代。 当服务器pipe理该ID时,我认为POST更合适。 分离这两个概念基本上告诉你如何处理它(即PUT是幂等的,所以它应该总是工作,只要有效载荷validation,POST总是创build,所以如果有冲突的ID,那么409会描述冲突) 。
毕竟,另一个潜在的治疗方法是使用PATCH。 PATCH被定义为改变内部状态的东西,不限于追加。
修补程序将通过允许您更新已经存在的项目来解决问题。 请参阅: RFC 5789:PATCH
为什么不接受202 ? 这是一个好的请求(200s),本身没有客户端错误(400s)。
来自10个状态码定义 :
“202已接受,请求已被接受处理,但处理尚未完成。”
…因为它不需要完成,因为它已经存在了。 客户不知道它已经存在,他们没有做错任何事情。
我倾向于抛出一个202,并返回类似的内容,以GET /{resource}/{id}
将返回。
怎么样208 – http://httpstatusdogs.com/208-already-reported ? 这是一个select吗?
在我看来,如果唯一的事情是重复的资源,不应该提出错误。 毕竟,在客户端或服务器端都没有错误。
在检查重复logging的正确代码时偶然发现了这个问题。
请原谅我的无知,但我不明白为什么大家都忽略了“多选”或“模棱两可”的代码“300”
在我看来,这将是build立一个非标准或特定系统供自己使用的完美代码。 我也可能是错的!