逻辑错误的 HTTP 400(错误请求),不是格式错误的请求语法

Posted

技术标签:

【中文标题】逻辑错误的 HTTP 400(错误请求),不是格式错误的请求语法【英文标题】:HTTP 400 (bad request) for logical error, not malformed request syntax 【发布时间】:2011-06-14 10:47:42 【问题描述】:

HTTP/1.1 specification (RFC 2616)对status code 400, Bad Request (§10.4.1)的含义有以下说法:

请求无法被理解 服务器由于语法错误。 客户不应该重复 无需修改即可请求。

如今,在一些基于 HTTP 的 API 中似乎有一种普遍的做法,即使用 400 来表示请求的逻辑而不是语法错误。我的猜测是 API 这样做是为了区分 400(客户端诱导)和 500(服务器诱导)。使用 400 表示非句法错误是否可以接受或不正确?如果可以接受,是否有关于 RFC 2616 的注释参考,可以更深入地了解 400 的预期用途?

例子:

Google Data Protocol, Protocol Reference, HTTP Status Codes

【问题讨论】:

Web 服务器为什么要关心语法错误? @leppie:例如,Web 服务器需要确保请求行和标头格式正确。 但这将是一个格式错误的客户端请求。 @leppie:客户端总是可以发送格式错误的请求。很明显,在这种情况下,服务器会以 400 响应。不清楚的是 400 是否是对格式良好但无效(每个应用程序)请求的合法响应。 @Atif Aziz:如果请求可以传递到某个后备服务器,那么它应该有 500 错误,除了众所周知的客户端错误,如身份验证失败或 404。 【参考方案1】:

想到状态 422 (RFC 4918, Section 11.2):

422(Unprocessable Entity)状态码表示服务器理解请求实体的内容类型(因此 415(Unsupported Media Type)状态码是不合适的),并且请求实体的语法是正确的(因此 400 (错误请求)状态代码不合适)但无法处理包含的指令。例如,如果 XML 请求正文包含格式正确(即语法正确)但语义错误的 XML 指令,则可能会发生这种错误情况。

【讨论】:

RFC 4918 是我不熟悉的 WebDAV RFC。可以在非 WebDAV HTTP 服务中重用 status code extensions to HTTP/1.1 吗? HTTP client compatibility 上的部分似乎表明它是。 IETF 是否维护所有(核心和扩展)HTTP 代码的明确列表,以确保基于 HTTP 构建的其他一些服务不会引入更多与其他扩展冲突的代码? 是的,没关系。这就是为什么有一个状态码注册表。见iana.org/assignments/http-status-codes。 422 非常接近。唯一的遗憾是它似乎特别关注不可处理的实体,而不是不可处理的请求。如果它只包含后者,它也将涵盖 HTTP GET 的情况,因为它没有实体。然而,422 的描述确实清楚地表明 400 不适合逻辑上无效的请求。【参考方案2】:

截至目前,HTTPbis 规范的最新草案,旨在替换和使 RFC 2616 过时,states:

400(Bad Request)状态码表示服务器不能或 不会处理请求,因为接收到的语法无效, 荒谬的,或超出服务器愿意的某些限制 处理。

此定义当然仍会发生变化,但它认可了广泛使用的以 400 响应逻辑错误的做法。

【讨论】:

【参考方案3】:

HTTPbis 将解决 400 Bad Request 的措辞,以便它也涵盖逻辑错误。所以 400 将包含 422。

来自https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-p2-semantics-18#section-7.4.1 “由于客户端错误(例如,格式错误的语法),服务器无法或不会处理请求”

【讨论】:

【参考方案4】:

尽管我也一直使用 400 来表示逻辑错误,但我不得不说在这种情况下返回 400 是错误的,因为规范读取的方式。这就是我这么认为的原因,逻辑错误可能是与另一个实体的关系失败或不满意,并且对另一个实体进行更改可能会导致相同的确切结果稍后通过。就像在该员工不存在时尝试(完全假设)将员工添加为部门成员(逻辑错误)。将员工添加为成员请求可能会失败,因为员工不存在。但是,在将员工添加到系统后,同样的请求可能会通过。

只要我的 2 美分……我们现在需要律师和法官来解释 RFC 中的语言 :)

谢谢你, 维什

【讨论】:

【参考方案5】:

可以说,您的请求中包含不正确的数据语法错误,即使您在 HTTP 级别(请求行、标头等)的实际请求在语法上是有效的。

例如,如果一个 Restful Web 服务被记录为接受具有自定义 XML 内容类型 application/vnd.example.com.widget+xml 的 POST,而您发送的是一些乱码纯文本或二进制文件,则将其视为语法错误似乎是合理的- 您的请求正文不是预期的形式。

不过,我不知道有任何官方引用来支持这一点,像往常一样,这似乎是为了解释 RFC 2616。

更新:注意RFC 7231 §6.5.1中的修改措辞:

400 (Bad Request) 状态码表示服务器不能或不会处理请求,原因是被认为是客户端错误,例如请求语法格式错误、请求消息帧无效或请求路由具有欺骗性)。

似乎比现在已经过时的RFC 2616 §10.4.1 更支持这个论点,RFC 2616 §10.4.1 只是说:

由于语法错误,服务器无法理解该请求。客户端不应该不加修改地重复请求。

【讨论】:

这当然是一种有趣的看待方式。使用无效查询字符串参数值的 GET 请求怎么办?您是否会说 400 可以使用,因为尽管查询字符串格式正确,但查询字符串参数值在应用程序的视图中存在语法错误? 好吧,这似乎与我给出的 POST 示例相比只是一小步,但我承认,当应用程序发现 URL 的某些部分无效时,对 GET 请求执行相同操作似乎有点恶心.在使用 HATEOAS(作为应用程序状态引擎的超文本引擎)的真正 Restful 界面中,资源的 URL 对客户端来说并不重要 - 客户端只是跟随来自其他资源的链接。但是大多数“REST”API 并不坚持这个原则,而是解释需要将哪些查询字符串参数附加到什么 URL,所以我猜在这样的服务中,400 响应是可以的。【参考方案6】:

在 Java EE 服务器上,如果您的 URL 引用了不存在的“web 应用程序”,则会返回 400。那是“语法错误”吗?取决于语法错误的含义。我会说是的。

在英语中,句法规则规定了词类之间的某些关系。例如,“Bob marries Mary”在语法上是正确的,因为它遵循模式 Noun + Verb + Noun。然而 “鲍勃婚姻玛丽”在语法上是不正确的,Noun + Noun + Noun。

简单 URL 的语法是 protocol + : + // + server + : + port 。据此,“http://www.google.com:80”在语法上是正确的。

但是“abc://www.google.com:80”呢?它似乎遵循完全相同的模式。但真的 这是一个语法错误。为什么?因为 'abc' 不是 DEFINED 协议。

关键是确定我们是否有 400 的情况需要的不仅仅是解析字符、空格和分隔符。它还必须识别什么是有效的“词性”。

【讨论】:

【参考方案7】:

这很难。

我认为我们应该;

    仅当客户端有权更改请求、标头或正文时返回 4xx 错误,这将导致请求以相同的意图成功。

    在未发生预期突变时返回错误范围代码,即未发生 DELETE 或 PUT 未更改任何内容。但是,POST 更有趣,因为规范说它应该用于在新位置创建资源,或者只处理有效负载。

使用 Vish 回答中的示例,如果请求打算将员工 Priya 添加到营销部门但未找到 Priya 或她的帐户已存档,那么这是一个应用程序错误。

请求运行良好,符合您的应用程序规则,客户端一切正常,ETag 匹配等等。

因为我们使用的是 HTTP,所以我们必须根据请求对资源状态的影响做出响应。这取决于您的 API 设计。

也许这是你设计的。

PUT  updated members list  /marketing/members

返回成功代码将表明资源的“替换”有效;对资源的 GET 会反映您的更改,但不会。

所以现在您必须选择一个合适的否定 HTTP 代码,这是棘手的部分,因为这些代码主要用于 HTTP 协议,而不是您的应用程序。

看了官方的HTTP代码,这两个看起来很合适。

409(冲突)状态码表示由于与目标资源的当前状态冲突,请求无法完成。此代码用于用户可能能够解决冲突并重新提交请求的情况。服务器应该生成一个有效载荷,其中包含足够的信息让用户识别冲突的来源。

500(内部服务器错误)状态代码表示服务器遇到了阻止其完成请求的意外情况。

虽然我们传统上认为 500 就像一个未处理的异常:-/

我不认为发明自己的状态码是不合理的,只要它始终如一地应用和设计。

这种设计比较容易处理。

PUT  membership add command  /accounts/groups/memberships/instructions/1739119

然后您可以设计您的 API 以始终成功创建指令,它返回 201 Created 和一个 Location 标头,并且该指令的任何问题都包含在该新指令中资源。

POST 更像是最后一次 PUT 到新位置。 POST 允许对消息进行任何类型的服务器处理,这会打开类似“操作成功失败”之类的设计。

您可能已经编写了一个执行此操作的 API,即网站。您 POST 付款表单,但由于信用卡号码错误而被成功拒绝。

对于 POST,是否返回 200 或 201 以及拒绝消息取决于是否创建了新资源并且是否可以在其他位置进行 GET。

综上所述,我倾向于设计需要更少 PUT 的 API,也许只是更新数据字段,以及调用规则和处理的操作和内容,或者只是有更高的预期失败机会,可以设计为发布说明。

【讨论】:

【参考方案8】:

就我而言:

我收到 400 个错误请求,因为我错误地设置了 content-type。我更改了内容类型,然后能够成功获得响应。

之前(问题):

ClientResponse response = Client.create().resource(requestUrl).queryParam("noOfDates", String.valueOf(limit))
                .header(SecurityConstants.AUTHORIZATION, formatedToken).
header("Content-Type", "\"application/json\"").get(ClientResponse.class);

之后(固定):

ClientResponse response = Client.create().resource(requestUrl).queryParam("noOfDates", String.valueOf(limit))
                .header(SecurityConstants.AUTHORIZATION, formatedToken).
header("Content-Type", "\"application/x-www-form-urlencoded\"").get(ClientResponse.class);

【讨论】:

以上是关于逻辑错误的 HTTP 400(错误请求),不是格式错误的请求语法的主要内容,如果未能解决你的问题,请参考以下文章

http请求400错误

HTTP 400 错误请求 - CURL PHP

HTTP 400 错误的请求!解决追加悬赏!

400 bad request

400 bad request

nginx http 400 错误的请求怎么解决