无效数据的REST响应代码

342

在以下情况下,应向客户端传递什么响应代码?

  1. 用户注册时传递的数据无效,比如错误的电子邮件格式
  2. 用户名/电子邮件已存在

我选择了403。我还发现下面的内容,我认为也可以使用。

维基百科:

412 Precondition Failed : 服务器未满足请求者对请求的其中一个前提条件

如果我应该使用403以外的代码,请提供建议。


可能是重复问题:https://dev59.com/i3A75IYBdhLWcg3w49UR - Genjo
我也在解决这个问题。 JAX-RS规范(2017)的第7章约束验证专门提供状态码建议,用于约束违规情况。 https://download.oracle.com/otn-pub/jcp/jaxrs-2_1-final-spec/jaxrs-2_1-final-spec.pdf?AuthParam=1546559699_8a1c42b298288ed02e280d293e710306 - burntsugar
1
422是答案。下面的被接受的答案是错误的。HTTP状态码很混乱,但全世界的开发人员已经长时间思考并选择了422,不是因为它很好,而是因为这是我们最好的选择。避免使用403、409、412等状态码。这些状态码有非常具体的技术含义,不应用于其他事情。例如,409用于写入/合并冲突,应提示客户端显示一些屏幕,说“糟糕,有人在此期间编辑了此记录”,并给您一个差异屏幕或其他东西。 - Stijn de Witt
这个回答由Mike Deck提供,应该被接受:https://dev59.com/3m025IYBdhLWcg3wQDdb#9132152 - Stijn de Witt
4个回答

369

400 在这两种情况下都是最好的选择。如果您想进一步澄清错误,可以更改原因短语或包含一个正文来解释错误。

412 - 先决条件失败是在使用最后修改日期和ETags进行有条件请求时使用的。

403 - 禁止访问是当服务器希望防止访问资源时使用的。

唯一可能的其他选择是 422 - 无法处理的实体。


11
虽然403在此情境下经常被使用,但它不仅限于访问控制,因为RFC2616-10.4.4指出:“服务器理解请求,但拒绝执行。[...] 如果服务器希望公开未能执行请求的原因,则应在实体中描述拒绝的原因。” 原因可能是无效数据。然而,422在这里更加适用。 - Yannick Loiseau
11
我们不要陷入文本批判的泥潭。例如,可以参考http://trac.tools.ietf.org/wg/httpbis/trac/ticket/294,该网站试图澄清403始终与授权有关。 - fumanchu
1
@fumanchu 这意味着如果用户没有权限访问请求的资源,则应返回403。但是我认为在用户无权访问资源时,401未经授权更为合适。 - Amit Patel
1
这个答案是错误的。400 表示服务器无法理解请求。在上述两种情况中,服务器都能够理解请求,但拒绝执行它(HTTP 403 的定义)。第二种情况也可以用 409(冲突)来处理。 - Greg Brown
1
@GregBrown 是的,你说得完全正确。对于这个状态码来说,“未经授权”是完全错误的。这很不幸,因为它使新开发人员的事情变得非常不清楚。也许更好的状态码名称应该是像“未登录”或“需要登录”之类的东西,因为显然人们很难区分“未经授权”和“未经身份验证”。此外,每个人都缩写为“Auth”,这使得它更加不清楚。 - Stijn de Witt
显示剩余14条评论

124

我建议使用422状态码。虽然它不是主要HTTP规范的一部分,但它是由公共标准(WebDAV)定义的,并且浏览器应该将其视为任何其他4xx状态码。

RFC 4918中得知:

422(无法处理的实体)状态码意味着服务器理解请求实体的内容类型(因此415(不支持的媒体类型)状态码不合适),请求实体的语法正确(因此400(错误的请求)状态码不合适),但无法处理包含的指令。例如,如果XML请求正文包含形式良好(即语法上正确),但语义上有误的XML指令,则可能出现此错误条件。


26
请注意,引用的文本指出,当请求实体在语法上格式正确但在语义上有错误时,应使用 422 响应。如果请求实体损坏了,则应使用 400 响应。 - Matty K
这似乎已经成为事实上的标准。许多API在这种情况下选择了422,并且它已经独立于WebDAV。 - Stijn de Witt
@MattyK 是100%正确的。这是需要区分的关键,也是为什么被接受的答案是错误的原因。400 Bad Request是指服务器无法解析请求时使用的状态码。虽然422最初是为WebDAV定义的,但它是4xx范围内最合适的响应代码,并且已经被许多API和框架用于验证错误,因此已成为标准。 - Stijn de Witt

107

如果请求无法正确解析(包括请求实体/正文),则适当的响应是400 Bad Request [1]。

RFC 4918指出,当请求实体在语法上格式正确但在语义上存在错误时,应使用422 Unprocessable Entity。因此,如果请求实体是乱码的(如坏电子邮件格式),请使用400;但如果它根本没有意义(如@example.com),请使用422。

如果问题是,如问题描述所述,用户名/电子邮件已经存在,则可以使用409 Conflict [2]并提供冲突描述和如何解决它的提示(在这种情况下,“选择不同的用户名/电子邮件”)。然而,在规范中写道,在HTTP授权方面的争论不考虑的情况下,也可以在此情况下使用403 Forbidden [3]。

当由客户端提供的先决条件请求标头(例如If-Match)计算为false时,使用412 Precondition Failed [4]。也就是说,客户端请求并提供了先决条件,完全知道这些先决条件可能会失败。412不应该突如其来地对客户端造成影响,并且不应与请求实体本身有关。


1
我应该注意更新的HTTP/1.1 RFCs:400 Bad Request,409 Conflict,403 Forbidden等位于http://tools.ietf.org/html/rfc7231; 412 Precondition Failed位于http://tools.ietf.org/html/rfc7232#section-4.2。 - Matty K
如果问题是,如问题所述,用户名/电子邮件已经存在,您可以使用409冲突[2]。不,这不是409 Conflict的意思。它非常明确地涉及写入冲突。因此,如果您的数据库事务失败,因为您尝试写入的记录在您读取和更改记录以及您编写它之间被另一个用户修改,那么这正是409的用途。考虑乐观锁等。它不适用于验证错误。在OP描述的两种情况下,422是最佳选择。 - Stijn de Witt
1
如果请求实体混乱不清(比如邮件格式错误),则使用400状态码;除非消息体预期是一个电子邮件地址,否则我不同意。对于消息体应该是JSON或XML的情况,400 Bad Request只有在JSON或XML格式不正确时才会使用,而不是在其中任何嵌入的数据出错时使用。基本上,当你编写一个服务器时,你要做一些像这样的事情try {const json = JSON.parse(request.body); if (! isValid(json)) { response.status = 422 } else { response.status = 200 } } catch (e) { response.status = 400} - Stijn de Witt

54

回应那些明显是恶意或“不可能发生”的请求,例如未通过CSRF检查或缺少请求属性等,返回418我是茶壶状态码是很有趣的。

2.3.2 418我是茶壶

使用茶壶煮咖啡的任何尝试都应该导致错误代码“418我是茶壶”。结果实体主体可以简短而粗壮。

为了保持足够的严肃性,我限制有趣错误代码的使用仅适用于不直接向用户公开的RESTful端点。


14
请实现API,使其对所有来自您的老板的请求返回“418我是茶壶” :) - vikarjramun
2
@vikarjramun,我已经建立了一个虚拟的REST,并制作了一个离线的生产版本(预发布)。现在我们的学生正在尝试创建有效的数据请求,但都是茶壶。我是“老板” - 但它也在工作。 - LenglBoy
2
这个RFC很愚蠢。只要你通过茶过滤器倒入杯子中,你就可以在茶壶中煮咖啡,就像用散茶一样。你也可以在咖啡壶中泡茶而没有任何问题。 - speciesUnknown
2
@gburton,这确实需要人类干预。在网络上,你肯定需要一台能够制作咖啡的设备。当然,咖啡壶和茶壶不应该响应418状态码。 - Jasper
2
如果HTTP状态码的其余部分是有序和清晰的规范,那么这个状态码本来会很有趣。但事实并非如此,它是一团糟,正如这个问题及其许多错误答案所证明的那样。因此,幽默是伟大的,但当真正的工作一团糟时,你开玩笑就不好了。HTTP状态码80%的关注点在内部。它们与HTTP有关。而实际上,我们需要应用程序级别的状态码。世界正在使用从WebDAV借来的“422 Unprocessable Entity”来处理基本的验证错误,这说明了什么。HTTP人只是忘记了……太忙于开玩笑了…… - Stijn de Witt

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