HTTP状态代码优先级和处理



假设一个web应用程序收到以下请求:

POST /some/endpoint HTTP/1.1
Host: <something>
Accept: application/json
Accept-Language: pt
Content-Type: application/json
If-Match: "blabla"
Some body
  • 如果服务器不支持HTTP1.1端点/some/endpoint不存在,则可能应首先检查前一个问题,并返回505而不是404
  • 如果碰巧服务器的端点都不接受POST,并且端点/some/endpoint不存在,则后者应该获得优先级,并且应该返回404而不是405
  • 如果Accept不能被提供并且主体不能被适当地解码/验证,那么406可能应该优先于400

在这些情况下,直觉可能就足够了。但是,还有无数其他情况不清楚两个非2XX状态代码中的哪一个应该首先被首选/检查。例如,如果Content-Type(导致415)或Accept-Language(406)都会失败,那么应该返回它们吗?415还是412?接着。。。

大部分时间错误是成对独立的:如果与抛出的一个错误相关的方面(例如特定的头值)是固定的,那么另一个错误的成功/错误状态将不会受到影响。在这些情况下,错误的"优先级"可能只是一个麻烦。但有时,这些错误可能是不独立于的:我可能有葡萄牙语HTML的资源,但只有英语JSON的资源(幽默我),所以如果客户希望我优先考虑Accept-Language而不是Accept,而我却相反,结果会很糟糕。

这个问题现在应该很明显了:对于哪些错误应该优先考虑,有什么标准吗

我没有遇到任何相关的RFC,甚至没有太多严肃和一般性的讨论。我知道网络机器图,它有点帮助,但主要似乎只是描述一个特定的(经过深思熟虑的)实现,而不是任何标准。

显然,你不能指望这个问题的答案是"不",即使这可能是正确的答案。

因此,让我来谈谈你的一个特别观点:

我可能有一个资源,比如葡萄牙语中的HTML,但JSON中只有英语(幽默我),所以如果客户希望我优先考虑Accept-Language而不是Accept,而我却相反,结果会很糟糕。

在您的示例中,您告诉服务器葡萄牙语JSON是好的,但所有其他组合都是相同的坏的。如果不是这样,你可以这样详细说明你的偏好:

Accept: text/json
Accept-Language: pt, en;q=0.1

然后,服务器可以乘以你的权重,英语JSON得到1×0.1=0.1,葡萄牙语HTML得到0×1=0,然后选择前者。

(旁注1:注册表中没有text/json介质类型。您可能想要application/json。)

(旁注2:415不支持的媒体类型不是您提到的场景的正确响应代码。它涉及请求主体。如果您不能遵守Accept标头,您可以用406不可接受来响应,就像用Accept-Language一样。)

TL;DR:规范赋予服务器处理请求的最终权限,甚至允许服务器忽略客户端请求的可接受格式。但是,规范指示服务器尽最大努力,并以最有助于客户端从错误中恢复的方式进行响应


规范提供了指导,即使它们没有(或不能)优先考虑所有可能的错误模式。

RFC 2616§10.4.7规定:

HTTP/1.1服务器被允许返回以下响应根据在要求在某些情况下,这甚至可能比发送406响应。鼓励用户代理检查传入响应以确定其是否可接受。

RFC 7231§3说:

原始服务器可能提供或能够生成,多个表示,每个表示都旨在反映目标资源的当前状态。在这种情况下,某些算法是源服务器使用来选择其中一个表示作为最适用于给定的请求,通常基于内容谈判

RFC 7231§3.4说:

请注意,在所有情况下,HTTP都不知道资源语义。源服务器响应请求的一致性。。。完全由所选择的实体或算法决定或者生成那些响应HTTP不注意这个人窗帘后面

RFC 7231§3.3说:

包含错误状态代码的响应消息通常包含表示错误条件的有效载荷,例如它描述了错误状态以及建议采取的下一步措施

RFC 2616§14.46说:

警告常规标头字段用于携带有关消息状态或转换的附加信息,这些信息可能不会反映在消息中。此信息通常用于警告缓存操作或应用于消息实体主体的转换可能缺乏语义透明度


(强调所有我的。)

RFC 7231的第3节赋予源服务器决定适当响应的最终权限,即使该响应令人反感。同时,部分3鼓励源服务器满足请求,或者提供其满足某些请求的通知(Vary),或者提供可选选项("被动协商")。

尽管服务器拥有最终的权限,但规范向我明确表示,响应应该帮助用户解决问题。在我看来,最好的错误代码是帮助用户最好地解决问题的代码!

考虑您的配对示例:

  • "如果服务器不支持HTTP1.1,并且端点/somet/endpoint不存在,则可能应该首先检查前一个问题,并返回505而不是404">

否。根据规范,HTTP1.1客户端可以通过协议降级从1.0服务器GET,因此这种版本协商由规范处理。发送一个404(或者301,如果知道的话),这样用户就可以纠正它

  • "如果碰巧服务器的端点都不接受POST,并且端点/some/端点不存在,则后者应该获得优先级,并且应该返回404而不是405

是的,404。如果你没有获得资源,那么方法就无关紧要了。

  • "如果不能提供Accept并且不能适当地解码/验证主体,则可能406应该优先于400

当您知道406适用时,千万不要发送400。您向客户提供的信息较少,这对客户的帮助较小。然而,根据RFC 7231§5.3.2:,原始服务器可以自由忽略Accept标头

如果[Accept]标头字段出现在请求中,但没有可用的表示形式响应的媒体类型被列为可接受原始服务器可以通过发送406(不是可接受)响应或通过处理响应,就好像它不受内容协商的约束一样。

  • "我可能有葡萄牙语的HTML资源,但只有英语的JSON资源(幽默地说),所以如果客户希望我优先考虑Accept Language而不是Accept,而我却相反,结果会很糟糕">

我不同意结果会很糟糕。参见RFC 7231§5.3.5:

源服务器可以通过将响应视为不受内容协商的响应来忽略[Accept-Language]报头字段,或者通过发送406(不可接受)响应来遵守报头字段。然而,不鼓励后者,因为这样做可能会阻止用户访问他们可能使用的内容(例如翻译软件)。

这种模式的规范语言不止一次出现"服务器可以无视[客户端请求的任何内容],将响应视为不受[规范的这一部分]约束,或者服务器可以接受[客户端请求]并发送[适用的错误代码]。但是,与其只发送一个难以理解的错误代码,不如发送一些易懂的东西">

归根结底,这是你的API。HTTP只提供了一个了解语义的窗口。记录你接受了什么,你如何回应,以及用什么。发送可理解的响应(HATEOAS很好),并在适用时发送最具体的可用错误代码。

最新更新