我正在构建一个允许客户端存储对象的服务器。这些对象完全构造在客户端,完整的对象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意味着“追加”操作,我不知道如果对象已经存在该怎么办。我应该将请求视为修改请求还是应该返回一些错误代码(?)?
答案 0 :(得分:843)
我的感觉409 Conflict
是最合适的,然而,当然在野外很少见到:
由于与资源的当前状态发生冲突,无法完成请求。此代码仅在预期用户可能能够解决冲突并重新提交请求的情况下才允许。响应主体应该包含足够的信息供用户识别冲突的来源。理想情况下,响应实体将包含足够的信息供用户或用户代理解决问题;但是,这可能是不可能的,也不是必需的。
最有可能发生冲突以响应PUT请求。例如,如果正在使用版本控制并且包含PUT的实体更改为与早期(第三方)请求所产生的资源冲突的资源,则服务器可能会使用409响应来指示它无法完成请求。在这种情况下,响应实体可能包含由响应Content-Type定义的格式的两个版本之间的差异列表。
答案 1 :(得分:73)
根据RFC 7231,可以使用 303 See Other 如果处理POST的结果等同于 表示现有资源。
答案 2 :(得分:67)
我个人使用WebDAV扩展程序422 Unprocessable Entity
。
422 Unprocessable Entity
状态代码表示服务器理解请求实体的内容类型(因此415 Unsupported Media Type
状态代码不合适),请求实体的语法正确(因此{{{ 1}}状态代码不合适但是无法处理包含的指令。
答案 3 :(得分:21)
游戏可能很晚,但在尝试创建REST API时偶然发现了这个语义问题。
为了扩展Wrikken的答案,我认为您可以根据具体情况使用409 Conflict
或403 Forbidden
- 简而言之,当用户无法解决问题时,请使用403错误冲突并完成请求(例如,他们无法发送DELETE
请求以明确删除资源),或者如果可能的话可以使用409。
10.4.4 403 Forbidden
服务器理解请求,但拒绝履行请求。 授权无效,请求不应重复。如果 请求方法不是HEAD,服务器希望公开 为什么请求没有得到满足,它应该描述原因 因为该实体的拒绝。如果服务器不想做 此信息可供客户使用,状态代码404(不是 找到了)可以代替使用。
如今,有人说“403”并且出现了权限或身份验证问题,但规范说它基本上是服务器告诉客户端它不会这样做,不要再问它了,这就是为什么客户不应该。
至于PUT
与POST
... POST
应该用于创建资源的新实例,当用户无法或不应创建标识符时对于资源。当资源的身份已知时使用PUT
。
9.6 PUT
...
POST和PUT请求之间的根本区别在于 反映在Request-URI的不同含义中。一个中的URI POST请求标识将处理随附的资源 实体。该资源可能是一个数据接受过程,一个网关 一些其他协议,或接受注释的单独实体。在 相反,PUT请求中的URI标识括起来的实体 请求 - 用户代理知道URI的意图和 服务器绝不能尝试将请求应用于其他资源。 如果服务器希望将请求应用于其他URI,
必须发送301(永久移动)响应;用户代理可以 然后自行决定是否重定向 请求。
答案 4 :(得分:13)
“302 Found”对我来说听起来合乎逻辑。并且RFC 2616表示除了GET和HEAD之外的其他请求可以回答(这肯定包括POST)
但它仍然让访问者通过这个URL来获取RFC的“Found”资源。为了使它直接进入真正的“找到”URL,应该使用“303 See Other”,这是有道理的,但强制另一个调用GET以下的URL。好的方面,这个GET是可缓存的。
我认为我会使用“303 See Other”。我不知道我是否可以回复身体中发现的“事物”,但我想这样做是为了将一次往返保存到服务器。
更新:重新阅读RFC后,我仍然认为不存在“4XX + 303 Found”代码应该是正确的。但是,“409 Conflict”是最好的现有答案代码(正如@ Wrikken所指出的那样),可能包括指向现有资源的Location标头。
答案 5 :(得分:9)
我认为你不应该这样做。
如您所知,POST是修改集合的,它用于创建新项目。所以,如果你发送id(我认为这不是一个好主意),你应该修改集合,即修改项目,但这是令人困惑的。
用它来添加一个没有id的项目。这是最好的做法。
如果要捕获UNIQUE约束(而不是id),您可以响应409,就像在PUT请求中一样。但不是ID。
答案 6 :(得分:6)
我认为对于REST,您只需要对该特定系统的行为做出决定,在这种情况下,我认为“正确”的答案将是这里给出的几个答案之一。如果您希望请求停止并且行为就好像客户端在继续之前犯了一个需要修复的错误,那么使用409.如果冲突真的不那么重要并且想要保持请求继续,那么通过重定向来响应客户端到找到的实体。我认为正确的REST API应该在POST之后重定向(或者至少提供位置头)到该资源的GET端点,所以这种行为将提供一致的体验。
编辑: 值得注意的是,您应该考虑PUT,因为您提供了ID。然后行为很简单:“我不在乎现在有什么,把这个东西放在那里。”意思是,如果没有任何东西,它就会被创造出来;如果有东西它会被替换。我认为当服务器管理该ID时,POST更合适。分离这两个概念基本上告诉你如何处理它(即PUT是幂等的,所以只要有效载荷有效,它总是有效,POST总是创建,所以如果有ID冲突,那么409会描述这个冲突)
答案 7 :(得分:5)
为什么不 202接受?这是一个OK请求(200s),本身没有客户端错误(400s)。
“202 Accepted。请求已被接受处理,但处理尚未完成。”
...因为它不需要完成,因为它已经存在。客户不知道它已经存在,他们没有做错任何事。
我倾向于投掷202,并返回与GET /{resource}/{id}
将返回的内容类似的内容。
答案 8 :(得分:5)
我会使用422 Unprocessable Entity
,当请求无效但问题不在语法或身份验证中时使用。{/ p>
作为反对其他答案的论据,使用任何非4xx
错误代码意味着它不是客户端错误,显然是。使用非4xx
错误代码来表示客户端错误根本没有任何意义。
似乎409 Conflict
是这里最常见的答案,但是,根据规范,这意味着资源已经存在,并且您应用于它的新数据与其当前状态不兼容。如果您要发送POST
请求(例如,已使用的用户名),则它实际上并不与目标资源冲突,因为目标资源尚未过帐。当存储的资源版本与请求的资源版本之间存在冲突时,这是一个专门用于版本控制的错误。它对于此目的非常有用,例如,当客户端缓存旧版本的资源并根据不再有条件有效的不正确版本发送请求时。 “在这种情况下,响应表示可能包含有助于根据修订历史合并差异的信息。”使用该用户名创建另一个用户的请求是无法处理的,与版本控制无关。
对于记录,422也是当您尝试使用已在使用的名称创建存储库时GitHub使用的状态代码。
答案 9 :(得分:5)
关于 上下文 的全部内容,还有谁负责在请求中重复(服务器或客户端或两者)
如果服务器只是指向重复项,请查看4xx:
要隐式处理重复项,请查看2XX:
如果服务器预计会返回某些东西,请查看3XX:
当服务器能够指向现有资源时,这意味着重定向。
如果以上操作还不够,在响应正文中准备一些错误消息始终是一个好习惯。
答案 10 :(得分:4)
另一种可能的治疗方法是使用PATCH。 PATCH被定义为改变内部状态的东西,不限于附加。
PATCH可以通过允许您更新现有项目来解决问题。请参阅:RFC 5789: PATCH
答案 11 :(得分:4)
您可以使用409 Conflict
如果您要从列表下方查看其他HTTPs
状态代码
1××参考
100 Continue
101 Switching Protocols
102 Processing
2××成功
200 OK
201 Created
202 Accepted
203 Non-authoritative Information
204 No Content
205 Reset Content
206 Partial Content
207 Multi-Status
208 Already Reported
226 IM Used
3××重定向
300 Multiple Choices
301 Moved Permanently
302 Found
303 See Other
304 Not Modified
305 Use Proxy
307 Temporary Redirect
308 Permanent Redirect
4××客户端错误
400 Bad Request
401 Unauthorized
402 Payment Required
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable
407 Proxy Authentication Required
408 Request Timeout
409 Conflict
410 Gone
411 Length Required
412 Precondition Failed
413 Payload Too Large
414 Request-URI Too Long
415 Unsupported Media Type
416 Requested Range Not Satisfiable
417 Expectation Failed
418 I’m a teapot
421 Misdirected Request
422 Unprocessable Entity
423 Locked
424 Failed Dependency
426 Upgrade Required
428 Precondition Required
429 Too Many Requests
431 Request Header Fields Too Large
444 Connection Closed Without Response
451 Unavailable For Legal Reasons
499 Client Closed Request
5××服务器错误
500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
505 HTTP Version Not Supported
506 Variant Also Negotiates
507 Insufficient Storage
508 Loop Detected
510 Not Extended
511 Network Authentication Required
599 Network Connect Timeout Error
答案 12 :(得分:2)
208 - http://httpstatusdogs.com/208-already-reported怎么样?这是一个选择吗?
在我看来,如果唯一的东西是重复资源,则不应该引发错误。毕竟,客户端或服务器端都没有错误。
答案 13 :(得分:2)
在检查重复记录的正确代码时偶然发现了这个问题。
请原谅我的无知,但我不明白为什么每个人都忽略了代码" 300"这清楚地说明了多重选择"或"不明确的"
在我看来,这将是构建非标准或特定系统供您自己使用的完美代码。我也错了!
答案 14 :(得分:2)
更有可能是400 Bad Request
6.5.1. 400 Bad Request
400(错误请求)状态代码表示服务器无法执行以下操作: 由于某些原因而不会处理请求 客户端错误(例如格式错误的请求语法,无效的请求) 消息框架或欺骗性请求路由)。
由于请求包含重复值(已存在的值),因此可以将其视为客户端错误。下一次尝试之前,需要更改请求。
考虑到这些事实,我们可以得出HTTP STATUS 400错误请求的结论。
答案 15 :(得分:1)
这是用户端故障,属于4xx组。这是正确的答案https://developers.rebrandly.com/docs/403-already-exists-errors
答案 16 :(得分:1)
错误 402,需要付款
I.E.这个资源已经存在,但如果你给我足够的钱,我会删除当前的资源并给你:D
...但是在 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses
处查看 Mozilla 对状态代码的定义作为这里没有人提供的更严肃的答案,451怎么样:由于法律原因不可用。您不能“合法地(根据您自己制定的条款和条件)”允许多人访问同一帐户信息
422 也是一个不错的选择,它是不可处理的实体 请求格式正确,但由于语义错误而无法遵循。因为它是一个完全有效的请求,但由于它在语义上等同于另一个条目,所以它不能被遵循。
答案 17 :(得分:0)
我知道已经有很多年了,但我会在这里留下我认为合适的答案。
我认为最好的方法是使用HTTP_306_RESERVED
。