在响应体中返回错误的HTTP 200 OK状态码

121

我在想,如果服务器端发生错误(错误详细信息将包含在响应体中),返回HTTP 200 OK是否正确。

例如:

  1. 我们发送HTTP GET
  2. 服务器端发生了意外情况。
  3. 服务器返回带有错误信息的HTTP 200 OK状态码作为响应(例如:{"status":"发生了一些错误"}

这是否是正确的行为?我们是否应该将状态码更改为不同于200的内容?


26
HTTP 200 表示在HTTP层面传输成功。这与您的“业务代码”的成功或失败无关。在这种情况下,HTTP 200 表示您的“业务代码错误信息”已成功传输 ;-)或者您可以让服务器响应HTTP 500,表示“内部错误”。这对于服务器上的技术性或不可恢复的问题更为典型。 - geert3
1
有人能确认一下吗?我和一些程序员交流过,听到了不同的意见。 - krzakov
3
请使用代码412。 - Dima Tisnek
12
看到两个相互矛盾的答案,这正是为什么 Stack Overflow 应该更加鼓励讨论的原因。 - wobbily_col
2
我看待这个问题基于预期的意图。如果调用方期望一个true或false,那么这是一个业务响应,并且应该返回200的true和false。另一方面,如果有一个API处理订单删除-对这个API的调用期望订单已经存在。如果不存在,那么在这里使用404就有意义了,而不是200。所以这不是一个一刀切的情况。 - MovieMe
显示剩余2条评论
8个回答

181

不,发送带有错误主体的200是非常不正确的。

HTTP是一种应用协议。200表示响应包含代表所请求资源状态的有效载荷。错误消息通常不是该资源的表示形式。

如果在处理GET时出现问题,则正确的状态代码为4xx(“你搞砸了”)或5xx(“我搞砸了”)。


18
不,我完全不同意第一部分的说法,即退回200是可以接受的。 - Julian Reschke
2
我认为在某些情况下,200 OK错误可能是适当的。但在这种情况下,客户端发出GET请求请求某些资源,但没有得到客户端要求的资源,而是得到了其他东西,这是错误的。如果是服务器错误5XX,如果是客户端错误4XX,例如所请求的资源不存在404。这是我的观点。 - FrAn
1
如果飞机上没有座位,应该返回什么状态码呢? - Alexanderius
20
最近我使用了一家财富500强的电子商务网站,我不同意这个答案中暗示不要使用200表示错误的说法。这家公司会将所有可以由Spring Java处理的错误都返回为200 OK,而在响应主体中你可以找到一个可用的“errors”属性。就这么简单,没有必要去尝试弄清楚哪个错误属于什么奇怪的HTTP状态码。 - prograhammer
14
“HTTP是一个应用协议。”尽管Wikipedia也同意这个定义,但必须指出,在此,“应用”指的是IP应用层 - Wikipedia列出了HTTP、Telnet、FTP和SSH等IP通信的应用程序。这与我的应用/业务逻辑不同,我的应用程序使用上述通信方式。因此,发送带有特定应用程序错误消息的HTTP 200是完全合理的。 - Jeff Ward
显示剩余13条评论

87

HTTP状态码是关于HTTP协议的内容。 HTTP 200 意味着在HTTP层面上传输正常(即请求在技术上正常,服务器能够正确响应)。请参见这个维基页面以获取所有代码及其含义的列表。

HTTP 200与您的“业务代码”的成功或失败无关。在您的示例中,只要没有技术问题阻止了业务逻辑的正常运行,HTTP 200就是指示您的“业务代码错误消息”已成功传输的可接受状态。

或者,如果服务器发生技术或不可恢复问题,则可以让服务器响应HTTP 5xx。如果传入请求存在问题(例如,错误的参数,意外的HTTP方法...),则可以响应HTTP 4xx。这些都表示技术性错误,而HTTP 200表示没有技术性错误,但不能保证业务逻辑错误。

总之:是有效的将错误消息(针对非技术问题)与HTTP状态200一起发送到您的http响应中。是否适用于您的情况取决于您。例如,如果客户端正在请求不存在的文件,则更像是404。如果服务器存在错误配置,则可能是500。如果客户端要求预订已满的飞机座位,则为200,您的“实现”将决定如何识别/处理此请求(例如,使用一个包含{"booking":"failed"}的JSON块)。


15
HTTP状态码是技术响应,而不是业务逻辑响应。我可以想象一个HTTP/REST API,在明显失败的业务逻辑情况下返回HTTP 200。例如:我是一个HTTP客户端,在预订飞机航班时,所有参数都正确,没有技术上的HTTP 4xx错误,服务器也健康,没有HTTP 5xx错误,但是飞机已经满员了。响应是一个JSON块,解释了这个事实: {"booking":"failed","reason":"plane is full"}。这完全可以返回HTTP 200,事实上(在我看来)这是唯一有效的HTTP状态码。 - geert3
49
“HTTP 200与您的“业务代码”的成功或失败无关” - 实际上,它是有关系的。HTTP是一个应用协议,它的状态代码反映了这一点。请参见RFC 7231,第6节:“状态代码元素是一个三位数的整数代码,给出尝试理解和满足请求的结果”,“满足”指的是成功处理请求。如果在应用程序的任何时候发生错误(尝试预订已满座位是否算错误是另一个讨论),则不应返回2xx状态代码。 - CodeCaster
19
请看一下4xx和5xx代码。它们明确用于技术问题,“理解并满足请求”应该从这个意义上理解。如果服务器无法理解请求,比如媒体类型不支持(415)或方法不允许(405),则无法理解请求。如果带宽限制超过了(509)或者网络读取超时(598),则服务器无法满足请求。但是在预订已满的飞机上座位与这些问题无关,即使飞机已满,请求也已被技术上理解和满足,因此应返回HTTP 200状态码。 - geert3
20
为了证明我的观点,举一个实际的例子:谷歌地图REST API。https://developers.google.com/maps/documentation/directions/#sample-response 所有状态码和错误信息都在JSON/XML块中返回,但是每种情况下HTTP 200都会在HTTP级别上返回。 - geert3
14
有趣的是,我试图编辑我的评论,因为我在完成之前不小心提交了它,结果SO回复了一个500错误的消息,“您只能在5分钟内编辑您的评论”。所以即使我的提交是“成功”的,错误也是在我这边,但它还是返回了500的错误代码。 ¯_(ツ)_/¯ 尽管对于这个特定的答案来说,我想支持你的观点是合理的,但也许并不是最好的选择。 - radixhound
显示剩余19条评论

24

我认为如果我们考虑现实生活,就可以解决这些问题。

糟糕的实践:

例子1:

Darling everything is FINE/OK (HTTP CODE 200) - (Success):
{
  ...but I don't want us to be together anymore!!!... (Error)
  // Then everything isn't OK???
}

例子2:

You are the best employee (HTTP CODE 200) - (Success):
{
  ...But we cannot continue your contract!!!... (Error)
  // Then everything isn't OK???
}

好的实践方法:
 Darling I don't feel good (HTTP CODE 400) - (Error):
{
  ...I no longer feel anything for you, I think the best thing is to separate... (Error)
  // In this case, you are alerting me from the beginning that something is wrong ...
}

这只是我的个人观点,每个人可以根据自己的需求或舒适程度来实现。

注:这个解释的灵感来自于一个很棒的朋友 @diosney


将响应代码与内容一起决定是不好的,对于“适度”你会返回什么? - Akin Zeman
是的,但现实很糟糕......如果你需要跨域,有些人会用JSONP回调来建模,这意味着状态码必须是200,错误信息在正文中。 - undefined

14
即使我想将业务逻辑错误作为HTTP代码返回,但对于这些错误,并没有适当的HTTP错误代码可用,而只能使用HTTP 200,因为它会误导实际错误情况。
所以,对于业务逻辑错误,HTTP 200是可以接受的。但所有被HTTP错误代码覆盖的错误都应该使用它们。
基本上,HTTP 200表示服务器正确地处理了用户请求(例如,如果飞机上没有座位,则无关紧要,因为用户请求已经被正确处理,甚至可以返回飞机上可用座位的数量,因此根本不会有业务逻辑错误或该业务逻辑可以在客户端完成。业务逻辑错误是一个抽象的含义,但HTTP错误更加明确。)

12

需要明确的是,应在协议适用的情况下使用 HTTP 错误代码,而不要使用 HTTP 状态代码来发送业务逻辑错误。

像“余额不足”、“没有可用出租车”、“用户名/密码不正确”等错误符合使用状态码为 200,并在响应体中进行应用程序特定的错误处理。

参见此软件工程回答

我认为明确分离协议更好。让 HTTP 服务器和 Web 浏览器各自做自己的事情,让应用程序做自己的事情。应用程序需要能够发出请求,并需要响应 - 它的请求方式以及如何解释响应的逻辑可以比 HTTP 层面更加复杂(或简单)。


6
这可能是个观点问题,但我不同意。这些错误是有原因存在的,没有必要重新发明一种发送标准错误的方式,只需要使用HTTP规范即可。如果请求格式不正确,发送400;如果用户无权访问资源,发送403等。https://httpstatuses.com/ - Alexandre Testu

6
我认为人们对应用程序逻辑与协议问题投入了太多的重量。重要的是响应应该是有意义的。如果您有一个API提供动态资源,并且请求X是从模板Y派生的,使用数据Z,而Y或Z当前不可用怎么办?那是业务逻辑错误还是技术错误?正确答案是,“谁在乎呢?”
您的API和响应需要是可理解和一致的。它应符合某种规范,该规范应定义有效响应的内容。符合有效响应的内容应返回200代码。未符合有效响应的内容应返回4xx或5xx代码,指示无法生成有效响应的原因。
如果您的规范对有效响应的定义允许{“error”:“invalid ID”},则它是成功的响应。如果您的规范没有做出这样的安排,则使用200代码返回该响应是一个错误决定。
我会对调用parseFoo函数进行类比。当您调用parseFoo(“invalid data”)时会发生什么?它会返回错误结果(可能为空)还是抛出异常?很多人会对是否采用其中一种方法持有近乎于宗教的立场,但最终由API规范决定。
引用:“状态码元素是一个三位数整数代码,用于表示尝试理解和满足请求的结果。”
显然,在“成功返回错误”是否构成HTTP成功或错误方面存在不同意见。我看到不同的人以不同的方式解释相同的规范。所以选择一边,当然可以,但也要接受无论如何整个世界都不会同意你的观点。我?我发现自己处于中间某个位置,但我将提供一些常识考虑。
1.如果服务器端代码在分派请求时捕获到意外异常,那听起来像是500内部服务器错误的定义。这似乎是OP的情况。 应用程序不应为意外错误返回200,但请参见第3点。 2.如果服务器端代码应该能够优雅地处理给定的无效输入,并且它不构成“异常”错误条件,则您的规范应适应提供有意义的诊断信息的HTTP 200响应。 3.最重要的是:拥有规范。使其保持一致。坚持下去。
在OP的情况下,听起来您有一个事实上的标准,即未处理的异常会产生具有可区分的响应主体的200。这不是理想情况,但是如果它没有破坏问题并且不会主动引起问题,那么您可能有更大更重要的问题要解决。

6
HTTP协议负责在互联网上传输数据。如果由于任何原因而导致传输中断,HTTP错误代码会告诉您无法发送的原因。正在传输的数据不是由HTTP错误代码处理的,只有传输方法。HTTP无法说:“好的,这个答案是无意义的,但是在这里。”它只是说“200 OK”。也就是说,我已经完成了将它传递给您的工作,剩下的就取决于您了。我知道这个问题已经回答过了,但我用自己能理解的话表达了一遍。对于任何重复之处,敬请谅解。

让我也说一下,程序员和开发人员有责任正确报告服务器端错误,并让用户知道服务器端存在什么样的问题。例如,抱歉,此页面已损坏,或者例如,我正在忙于更新我的数据库,马上就会回来。 - Chris Groves

0
我最近正在开发一个在IIS服务器上运行的ASP.net项目,如果有任何问题,IIS服务器会在响应中附加默认的HTML页面。
问题是,该项目是一个只与JSON通信的API服务,所以我们希望避免IIS服务器这样做。更糟糕的是,操作系统是繁体中文,所以IIS服务器的默认HTML页面是使用BIG-5编码而不是UTF-8!这让我们的前端开发人员非常困惑,因为响应变成了乱码。
可悲的是,我没有权限将我们从使用IIS服务器切换到其他方式,也无法更改配置。因此,对于后端可以捕获的所有业务错误和异常,我们发送200 OK,并在响应的JSON中包装实际的HTTP状态码/消息。
我认为,尽管这违反了传统的HTTP状态码定义,但它避免了对服务器和客户端之间响应的不必要影响。
我不是一个前端专家,所以不确定,但我猜这也消除了需要同时查看HTTP状态和主体来确定请求是否成功的需求。

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