我正在构建一个允许客户端存储对象的服务器。 这些对象在客户端完全构造,完整的对象ID对于对象的整个生命周期是永久的。
我已经定义了API,以便客户端可以使用PUT创建或修改对象:
1 2 3 4
| PUT /objects/{id} HTTP/1.1
...
{json representation of the object} |
{id}是对象ID,因此它是Request-URI的一部分。
现在,我也在考虑允许客户端使用POST创建对象:
1 2 3 4
| POST /objects/ HTTP/1.1
...
{json representation of the object, including ID} |
由于POST意味着"追加"操作,我不知道如果对象已经存在该怎么办。 我应该将请求视为修改请求还是应该返回一些错误代码(哪个)?
-
截至2016年6月,当电子邮件存在时,FB公开宣布注册200
-
尝试使用已在使用的名称创建资源(团队/存储库)时,Github API返回422
-
这取决于您是否认为对象的存在是错误的。 如果您处理附加,则200或204是最合适的响应代码。
我的感觉409 Conflict是最合适的,然而,当然在野外很少见到:
The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.
Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.
-
为什么不去400 Bad Request?对我来说,这看起来有点像验证错误(您使用非法ID提供错误的有效负载)。
-
400 =>"由于语法格式错误,服务器无法理解请求"。服务器完全理解,但由于冲突而无法遵守。该请求没有任何问题。语法,只有数据问题。 400会立即让我相信我正在使用的整个机制是有缺陷的,而不仅仅是数据。
-
请注意,规范的这一部分引用的资源是客户端尝试附加新对象的集合。如果服务器接受了更改集合的请求,则会导致其由集合中的先前更改(即版本冲突的定义)导致的不一致状态。这让我意识到这个答案可能是正确的。在这种情况下,服务器还可以在响应中包括引用现有资源的Link头或实体主体中的超链接。
-
@Wrikken那不再正确。 RFC 40031中的HTTP 400已更改为"服务器无法或不会处理请求,因为某些内容被认为是客户端错误(例如,格式错误的请求语法,无效的请求消息框架或欺骗性请求路由)。"我不是说400在这种情况下是正确的用法,但它可能是正确的400新定义。
-
@javajavajavajavajava:在我看来,重复的数据并不是一个"客户端错误",但这当然是旁观者的眼睛。
-
不是。两种定义中400的意图是告诉客户端因为请求不好而停止尝试。
-
我返回HTTP 409,带有指向现有/冲突资源的Location标头。
-
我仍然认为409适合400. w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10
-
根据规范,暗示错误409不能由POST请求返回(正确使用时),因为它声明它应该在与目标资源冲突时返回。由于目标资源尚未发布,因此无法发生冲突,因此使用409 Conflict进行回复没有任何意义。
-
@GrantGryczan你正在发布的集合本身就是一个目标资源。在这种情况下,POST /objects/。
-
当存储的资源版本与请求的资源版本之间存在冲突时,这是一个专门用于版本控制的错误。它对于此目的非常有用,例如,当客户端缓存旧版本的资源并根据不再有条件有效的不正确版本发送请求时。"在这种情况下,响应表示可能包含有助于根据修订历史合并差异的信息。"
-
这也是Auth0库在您尝试注册时已存在用户的情况下使用的代码。
-
@dhj可能是因为Auth0的人读过这个页面
-
发出409而不是通用400还允许恶意用户查找系统中已存在的电子邮件。
-
@GrantGryczan毫无疑问,POST请求无法返回错误409。它可以用于版本控制,但不是必需的。根据规范:"响应PUT请求最有可能发生冲突"并不意味着禁止POST。在这里的许多其他答案中重复你的意见并没有使它更正确。 RFC7231(在原始问题发布4年后发布)表明,对于现有资源,建议使用error 303 See Other,报告它的位置。对于追加,如OP所述,200或204更合适。
-
没有什么是"禁止的"。它们只是规格。你不必遵循它们。但如果你愿意,我认为422更合适。而且,303是重定向代码。你没有重定向到任何东西。
-
@MavrickLaakso如果您有真正的安全性,那么在任何系统中发现资源存在的能力都不是问题。你所暗示的是"通过默默无闻的安全",这通常被认为不是真正的安全。此外,如果用户尝试注册服务的电子邮件地址,只需返回400无效,用户需要知道已收到电子邮件,以便他们可以选择其他内容。一般而言,您可以在尝试创建新资源之前始终对用户进行身份验证。
-
@JWAspin这里没有正确的答案,两者都有权衡。如果我是恶意用户,并且系统尊重状态代码,则409允许我至少编译一个有效电子邮件列表以试图破解。 400不一定允许我这样做。这就是我试图提出的观点。
根据RFC 7231,可以使用303 See Other MAY如果处理POST的结果等于a
表示现有资源。
-
在我看来,这可能是一个公认的答案。虽然"MAY"表示完全可选项,但它是官方RFC 7231文档建议的唯一响应代码。
-
这是最RESTful的答案。
-
只是FYI .. IE没有返回303的响应正文,如果这很重要。
-
我认为背景很重要。例如:返回303意味着需要重定向到找到的资源。这在服务器到服务器调用中可能有意义,但如果您正在运行用户注册过程,那么根本就没有任何意义。
-
对不起,我正在低估这个。 HTTP 300s是关于重定向的,并且重定向到可能具有不同属性的另一个对象将是非常误导的。
-
你不必抱歉。但是,如果表示等同于现有资源,它如何具有不同的属性?即使它有,重定向将如何误导? OP说:我不知道如果对象已经存在该怎么办。它实际上是"相同"的对象。为什么重定向会产生误导?你在谈论另一个在OP的头脑中显然不是的对象。
-
@Nullius:非常正确;我忽略了问题的最后细节。道歉。
-
@Nullius:除非编辑问题,否则不要让我撤消我的downvote。我建议您澄清一下,您的答案仅适用于物品何时存在的情况?我怀疑我不是唯一忽视这个细节的人。
-
在语义上,客户端说"创建此",服务器通过说"转到此处"来响应。谈话没有任何意义。这几乎就像服务器告诉客户"发布到这个位置"。对于GET请求或POST,在服务器响应"Ok我创建它并且它在这里"的情况下,300s更合适。
-
@Sinaesthetic:我认为在某种情况下让客户知道'嘿,这个已经存在,你可以在这里找到它'是完全没错的。我提到的'我创建它并且它已经在这里',应该提供带有201 Created或200 Ok状态代码的Content-Location标头。因此,如果已创建某些内容,则只有200和201是有效的返回代码。这也在我在原始答案中链接的相同RFC中明确说明。我同意303在所有情况下或所有商业案例中可能都不是正确的答案。这也是他们在RFC中强调MAY的原因。
-
但是如果URL相同但方法不同怎么办?例如,你不能在这里发帖,因为你已经发布了它,但是你可以在这里发布它吗?
我个人使用WebDAV扩展422 Unprocessable Entity。
根据RFC 4918
The 422 Unprocessable Entity status code means the server understands the content type of the request entity (hence a 415 Unsupported Media Type status code is inappropriate), and the syntax of the request entity is correct (thus a 400 Bad Request status code is inappropriate) but was unable to process the contained instructions.
-
这是一个有趣的想法,促使我最终阅读了WebDAV RFC。但是,我认为422的含义是请求和包含的实体在语法上是正确的,但在语义上没有意义。
-
@vmj正好;例如,GitHub的API在POST请求中为格式错误的JSON主体吐出422。
-
格式错误的JSON不是语法上正确的实体,所以422让我觉得奇怪......
-
Ruby on Rails多年来使用422来处理无效数据。
-
当您尝试创建现有资源(例如,同名的存储库)时,GitHub的API会返回422状态代码。它在响应中返回的消息是"Validation Failed"
-
我不会这样做。从答案中引用的相同URL:"例如,如果XML请求主体包含格式正确(即语法正确)但语义错误的XML指令,则可能会出现此错误情况。"这是一个不可处理的实体的真正含义,与您使用有效语法和语义发送完全有效的请求实体的情况不同,但唯一的问题是它与现有实体冲突。实际上,如果请求实体的语义无效,则根本不应存在类似的现有实体。
-
添加到Tamer评论,如果第一个请求首先出现,那么它会成功,如果这在语义上是正确的,这是不可能的。因此,正确的语义不适用于此。
-
@Tamer为什么会这样?命令"Please create object xy"在语法上是正确的。仅当可以创建对象xy时,它在语义上是正确的。如果对象xy已经存在,则无法再创建它,因此这是一个语义错误。
-
如果使用DAV扩展,为什么不208 Already Reported?
可能在游戏后期,但我在尝试制作REST API时偶然发现了这个语义问题。
为了扩展Wrikken的答案,我认为你可以使用409 Conflict或403 Forbidden,具体取决于具体情况 - 简而言之,当用户无法解决冲突并完成请求时,请使用403错误(例如,他们无法发送DELETE请求以显式删除资源),或者如果可能的话可以使用409。
10.4.4 403 Forbidden
The server understood the request, but is refusing to fulfill it.
Authorization will not help and the request SHOULD NOT be repeated. If
the request method was not HEAD and the server wishes to make public
why the request has not been fulfilled, it SHOULD describe the reason
for the refusal in the entity. If the server does not wish to make
this information available to the client, the status code 404 (Not
Found) can be used instead.
如今,有人说"403"并且出现了权限或身份验证问题,但规范说它基本上是服务器告诉客户端它不会这样做,不要再问它,这就是为什么客户端不应该"T。
至于PUT与POST ... POST应该用于在用户无法或不应该为资源创建标识符时创建资源的新实例。当资源的身份已知时使用PUT。
9.6 PUT
...
The fundamental difference between the POST and PUT requests is
reflected in the different meaning of the Request-URI. The URI in a
POST request identifies the resource that will handle the enclosed
entity. That resource might be a data-accepting process, a gateway to
some other protocol, or a separate entity that accepts annotations. In
contrast, the URI in a PUT request identifies the entity enclosed with
the request -- the user agent knows what URI is intended and the
server MUST NOT attempt to apply the request to some other resource.
If the server desires that the request be applied to a different URI,
it MUST send a 301 (Moved Permanently) response; the user agent MAY
then make its own decision regarding whether or not to redirect the
request.
-
我认为403 Forbidden暗示,即使用户经过身份验证,他也无权执行请求的操作。我不会将它用于验证错误。示例:未登录,我尝试删除某些内容。服务器发给我401 Unauthorized(这个名字很糟糕,应该是401 Unauthenticated)。我登录后再试一次。这次服务器检查我的权限,看到我不被允许并返回403 Forbidden。另见这个问题。
-
嗯...是的。这里的想法是直接告诉用户他们的授权在OP的用例中使资源不可变 - 它已经存在,你无权做任何事情来解决冲突,不要再尝试创建资源。
-
根据规范,暗示错误409不能由POST请求返回(正确使用时),因为它声明它应该在与目标资源冲突时返回。由于目标资源尚未发布,因此无法发生冲突,因此使用409 Conflict进行回复没有任何意义。
-
我不会推断POST不能返回409错误,事实上,我推断相反的结果是因为"响应PUT请求时最有可能发生冲突"。似乎表明其他请求方法也可以使用此代码。此外,"响应主体应该包含足够的信息以便用户识别冲突的来源。理想情况下,响应实体将包含足够的信息供用户或用户代理来解决问题;但是,这可能是不可能的,并且是不需要。" (webdav.org/specs/rfc2616.html#status.409)
"302 Found"听起来合情合理。并且RFC 2616说它可以回答除GET和HEAD之外的其他请求(这肯定包括POST)
但是它仍然让访问者通过这个URL来获取RFC的"Found"资源。为了使它直接进入真正的"找到"URL,应该使用"303 See Other",这是有道理的,但强制另一个调用GET以下的URL。好的方面,这个GET是可缓存的。
我想我会用"303见其他"。我不知道我是否可以回复身体中发现的"东西",但我想这样做是为了将一次往返保存到服务器上。
更新:重新阅读RFC之后,我仍然认为不存在的"4XX + 303 Found"代码应该是正确的。但是,"409 Conflict"是现有的最佳答案代码(由@ Wrikken指出),可能包括指向现有资源的Location头。
-
3xx状态用于重定向
-
"请求的资源暂时驻留在不同的URI下。"来自w3.org/Protocols/rfc2616/rfc2616-sec10.html
-
恕我直言,"307临时重定向"是真正的临时重定向。"302"含糊不清,但"FOUND !!"这是真正想要的消息。最明确的妥协是HTTP语义上的"303 See Other"。我会选择"303 See Other"。
-
3xx代码不是错误!
-
@DavidVartanian Hum ......我在这里看不到错误。客户端发送了一个正确的请求,但是如何说"对不起,但你在这里创建的内容已经存在"?似乎是一个3xx的工作。这对我来说不是4xx,因为没有客户端错误。
-
那是一个错误!!什么是你的错误?客户端尝试执行操作,但由于特定原因无法完成操作:您尝试创建的实体已存在!
-
谁弄错了?服务器还是客户端?
-
我真的认为不是。客户没有错,所以这里没有4xx。服务器没有错,所以这里没有5xx。
-
@DavidVartanian感谢您的讨论。更新了409的答案。即使不知道这是不可能的,客户也不应该要求不可能的东西。
-
根据规范,暗示错误409不能由POST请求返回(正确使用时),因为它声明它应该在与目标资源冲突时返回。由于目标资源尚未发布,因此无法发生冲突,因此使用409 Conflict进行回复没有任何意义。
这完全取决于上下文,以及谁负责在请求中重复(服务器或客户端或两者)
如果服务器只是指向副本,请查看4xx:
-
400 Bad Request - 当服务器不处理请求时,因为它显然是客户端故障
-
409 Conflict - 如果服务器不处理请求,但原因不是客户端的错误
-
...
要隐式处理重复项,请查看2XX:
如果服务器需要返回一些内容,请查看3XX:
当服务器能够指向现有资源时,它意味着重定向。
如果上述还不够,那么在响应正文中准备一些错误消息总是一个好习惯。
-
请求不是复制资源,而是将数据附加到一个资源。在我看来,你的是最好的答案。
我认为你不应该这样做。
如您所知,POST是修改集合的,它用于创建新项目。所以,如果你发送id(我认为这不是一个好主意),你应该修改集合,即修改项目,但这是令人困惑的。
用它来添加一个没有id的项目。这是最好的做法。
如果要捕获UNIQUE约束(而不是id),则可以响应409,就像在PUT请求中一样。但不是身份证。
-
具有连接表关系的对象怎么样?假设我们将account,product和account_product作为数据库表。我想在帐户中添加产品,因此我希望使用product_id发布到/ account / {id} / product。如果只允许一个帐户 - 产品关系,我应该返回什么?
-
忘记数据库表。假设一个产品只能与一个帐户相关......那么它就是一对多的关系。所以,POST / product / {id}与{'account':account_id}。如果你将最大基数设置为'1'(一对一的关系)....为什么它们分开了休息对象?基数错误只是400错误。把事情简单化。我希望我理解你的问题。
-
我刚刚也提出了这个问题,对我而言,ID不是数据库中的技术ID,而是公司代码之类的东西。在此应用程序中,经理用户可以创建公司,并且必须为他们提供代码。这是用户的公司ID,尽管DB表还具有技术ID。所以在我的情况下,如果已存在相同的公司代码,我将返回409。
-
@partkyle停止使用PK作为公共ID !!
-
一些实体对它们有唯一的约束,而不仅仅是id。与帐户一样,如果用户未提供用户名,则无法创建帐户。添加一个没有用户名的帐户显然是不可能的
-
当实体具有唯一约束时,服务器必须响应409 CONFLICT。我认为这不是问题的关键。当您输入一个项目并且该项目已经存在(id)时,您可以对其进行修改,但如果您复制一个Unique约束,则会有一个409。
我认为对于REST,你只需要对该特定系统的行为做出决定,在这种情况下,我认为"正确"的答案将是这里给出的几个答案之一。如果您希望请求停止并且行为就好像客户端在继续之前犯了一个需要修复的错误,那么使用409.如果冲突真的不那么重要并且想要保持请求继续,那么通过重定向来响应客户端到找到的实体。我认为正确的REST API应该在POST之后重定向(或者至少提供位置头)到该资源的GET端点,所以这种行为将提供一致的体验。
编辑:
值得注意的是,您应该考虑PUT,因为您提供了ID。然后行为很简单:"我不在乎现在有什么,把这个东西放在那里。"意思是,如果没有任何东西,它就会被创造出来;如果有东西它会被替换。我认为当服务器管理该ID时,POST更合适。分离这两个概念基本上告诉你如何处理它(即PUT是幂等的,所以只要有效载荷有效,它总是有效,POST总是创建,所以如果有ID冲突,那么409会描述这个冲突) 。
-
根据规范,暗示错误409不能由POST请求返回(正确使用时),因为它声明它应该在与目标资源冲突时返回。由于目标资源尚未发布,因此无法发生冲突,因此使用409 Conflict进行回复没有任何意义。
-
有争议的imo。如果您发布到/ users,则资源是集合而不是单个记录/ users / {id}
-
当存储的资源版本与请求的资源版本之间存在冲突时,这是一个专门用于版本控制的错误。它对于此目的非常有用,例如,当客户端缓存旧版本的资源并根据不再有条件有效的不正确版本发送请求时。"在这种情况下,响应表示可能包含有助于根据修订历史合并差异的信息。"
-
我确实喜欢你使用PUT的建议。
我会使用422 Unprocessable Entity,当请求无效但问题不在语法或身份验证中时使用。
作为反对其他答案的论据,使用任何非4xx错误代码意味着它不是客户端错误,显然是。使用非4xx错误代码来表示客户端错误根本没有任何意义。
似乎409 Conflict是这里最常见的答案,但是,根据规范,这意味着资源已经存在,并且您应用于它的新数据与其当前状态不兼容。如果您要发送POST请求(例如,已使用的用户名),则它实际上并不与目标资源冲突,因为目标资源尚未过帐。当存储的资源版本与请求的资源版本之间存在冲突时,这是一个专门用于版本控制的错误。它对于此目的非常有用,例如,当客户端缓存旧版本的资源并根据不再有条件有效的不正确版本发送请求时。"在这种情况下,响应表示可能包含有助于根据修订历史合并差异的信息。"使用该用户名创建另一个用户的请求是无法处理的,与版本控制无关。
对于记录,422也是当您尝试使用已在使用的名称创建存储库时GitHub使用的状态代码。
为什么不接受202?这是一个OK请求(200s),本身没有客户端错误(400s)。
来自10个状态码定义:
"202 Accepted. The request has been accepted for processing, but the processing has not been completed."
...因为它不需要完成,因为它已经存在。客户不知道它已经存在,他们没有做错任何事。
我倾向于投掷202,并返回与GET /{resource}/{id}将返回的类似内容。
-
这个答案是对的。 202表示服务器未发现请求有问题,但选择在响应后处理请求。这也意味着它希望处理成功。在我们的例子中,服务器知道处理将失败,因此202是错误的响应。
-
202的示例将是队列或订阅。换句话说,如果您此时查询请求,则可能无法立即获得请求的结果。
-
如果服务器仍在处理请求,这将是适当的。 200或204会更常见。由于OP正在发出追加请求,因此对象的存在是预期的条件而不是错误。
-
对客户说这个请求被接受是没有意义的,因为你已经知道它不是!
另一种可能的治疗方法是使用PATCH。 PATCH被定义为改变内部状态并且不限于附加的东西。
PATCH允许您更新现有项目,从而解决问题。请参阅:RFC 5789:PATCH
-
补丁就像PUT,但不是完全替代品。它用于修改资源的一部分,如添加,删除或修改资源的单个元素,而不是整体替换它。
更可能是400 Bad Request
6.5.1. 400 Bad Request
The 400 (Bad Request) status code indicates that the server cannot or
will not process the request due to something that is perceived to be
a client error (e.g., malformed request syntax, invalid request
message framing, or deceptive request routing).
由于请求包含重复值(已存在的值),因此可将其视为客户端错误。需要在下次尝试之前更改请求。
通过考虑这些事实,我们可以得出HTTP状态400错误请求。
在检查重复记录的正确代码时偶然发现了这个问题。
原谅我的无知,但我不明白为什么每个人都忽略了代码"300",清楚地说"多选"或"暧昧"
在我看来,这将是构建非标准或特定系统供您自己使用的完美代码。我也错了!
https://tools.ietf.org/html/rfc7231#section-6.4.1
-
我的理解:"状态代码表明目标资源有多个表示...正在提供有关备选方案的信息,以便用户(或用户代理)可以通过将其请求重定向到其中一个或多个来选择首选表示标识符"我们明确地试图阻止多个表示。没有选择。客户无法选择。客户端应该使用不同的id重新提交。话虽如此,还应考虑是否应在客户端与服务器之间生成唯一ID。
-
在语义上,客户端说"创建此",服务器通过说"转到此处"来响应。谈话没有任何意义。这几乎就像服务器告诉客户"发布到这个位置"。对于GET请求或POST,在服务器响应"Ok我创建它并且它在这里"的情况下,300s更合适。
-
3xx错误代码不能指示错误。
208 - http://httpstatusdogs.com/208-already-reported怎么样?这是一个选择吗?
在我看来,如果唯一的东西是重复资源,则不应该引发错误。毕竟,客户端或服务器端都没有错误。
-
这是没有选项,因为您想要附加某个已存在ID的项目。所以你试着添加一些东西,但这已经存在了。 OK仅在数据集增长时才适用。追加东西 - >好的我什么都没有添加。我觉得不合适。
-
2xx错误代码不能指示错误。
-
正如我所说,我不认为这是一个错误。但我明白了@martin的观点
-
如果未成功创建资源,则根据定义存在错误。
-
POST也用于附加数据。这是定义,而不是错误。