后端API接口的错误信息返回规范
前言
最近我司要制定开发规范。在讨论接口返回的时候,后端的同事询问我们前端,错误信息的返回,前端有什么意见?
所以做了一些调研给到后端的同事做参考。
错误信息返回
在使用API
时无可避免地会因为各种情况而导致接口返回错误的信息。比如指定的query
参数错误,又或者method
不支持等,这些情况都会返回相关的错误信息。另外服务器不稳定或者停止运行了,也必须将错误信息返回。
显然,当错误发生的时候,只是笼统地返回“发生了错误”是不行的。如果前端不了解发生了什么错误,也就不知道该怎么去调试,怎么去修复这个bug
。所以说,必须向前端返回尽可能多的信息,以便前端找到出错的地方解决问题。
通过HTTP状态码表示出错信息
首先必须选择合适的HTTP
状态码,之前我司的后端API
没有遵循这个规则。例如API
无论如何访问,都会返回200
状态码,只在返回消息体中的描述是否发生错误。
HTTP/1.1 200 OK
Content-Type: application/json
{
"error": {
"code": 2002,
"message": "Invalid parameter"
}
}
虽然这么处理前端也能理解API
的错误信息,但由于HTTP
协议已经完整地定义了各个状态码所表示的含义,所以返回恰当的状态码才能提高前端正确识别错误的可能性。
如果出错了,仍然返回200
状态码,有可能导致前端的处理发生混乱,这种情况要一定要禁止。特别是通用的API
,基本上都是先看状态码再决定下一步的处理,如果没有返回正确的状态码,就会导致前端无法执行适当的方法去处理,从而引发各种不必要的问题。而且这种做法没有尽可能地运用HTTP
协议,也给前端编写错误处理增加了难度。
- 状态码分类
状态码 | 含义 |
---|---|
1xx | 消息 |
2xx | 成功 |
3xx | 重定向 |
4xx | 前端原因引起的错误 |
5xx |
下面主要来了解一下4xx
和5xx
的错误码:
4xx
-前端发生了错误
4xx
的状态码主要是用于描述因前端请求的问题而引发的错误,也就是说服务器端不存在出错问题,但服务器端无法理解前端的请求,或者能理解但无法处理的请求。这一类的错误,统一使用4xx
错误码。
状态码 | 名称 | 说明 |
---|---|---|
400 | Bad Request | 表示其他错误,就是4xx都无法描述的前端发生的错误 |
401 | Authentication | 表示认证类型的错误 |
403 | Authorization | 表示授权的错误(认证和授权的区别在于:认证表示“识别前来访问的是谁”,而授权则是“赋予特定用户执行特定操作的权限”) |
404 | Not Found | 表示访问的数据不存在 |
405 | Method Not Allowd | 表示可以访问接口,但是使用的HTTP方法不允许 |
406 | Not Acceptable | 表示API不支持前端指定的数据格式 |
408 | Request Timeout | 表示前端发送的请求到服务器所需的时间太长 |
409 | Confilct | 表示资源发生了冲突,比如使用已被注册邮箱地址注册时,就引起冲突 |
410 | Gone | 表示访问的资源不存在。不单表示资源不存在,还进一步告知该资源该资源曾经存在但目前已消失 |
413 | Request Entity Too Large | 表示请求的消息体过长而引发的错误 |
414 | Request-URI Too Large | 表示请求的首部过长而引发的错误 |
415 | Unsupported Media Type | 表示服务器端不支持客户端请求首部Content-Type里指定的数据格式 |
416 | Range Not Satisfiable | 表示无法提供Range请求中的指定的那段包体 |
417 | Expectation Failed | 表示对于Expect请求头部期待的情况无法满足时的响应码 |
421 | Misdirected Request | 表示服务器认为这个请求不该发给它,因为它没能力处理 |
426 | Upgrade Required | 表示服务器拒绝基于当前HTTP协议提供服务,通过Upgrade头部告知客户端必须升级协议才能继续处理 |
428 | Precondition Required | 表示用户请求中缺失了条件类头部,例如If-Match |
429 | Too Many Requests | 表示客户端发送请求的速率过快 |
431 | Request Header Fields Too Large | 表示请求的HEADER头部大小超出限制 |
451 | Unavailable For Legal Reasons | 表示由于法律原因不可访问 |
5xx
-服务器端发生错误
5xx
状态码表示错误由服务器端的问题引发的。
状态码 | 名称 | 说明 |
---|---|---|
500 | Internal Server Error | 表示服务器内部错误,且不属于以下错误类型 |
501 | Not Implemented | 表示服务器不支持实现请求所需要的功能 |
502 | Bad Gateway | 代理服务器无法获取到合法资源 |
503 | Service Unavailable | 服务器资源尚未准备好处理当前请求 |
504 | Gateway Timeout | 表示代理服务器无法及时的从上游获得响应 |
505 | HTTP Verson Not Supported | 表示请求使用的HTTP协议版本不支持 |
507 | Insufficient Storage | 表示服务器没有足够的空间处理请求 |
508 | Loop Detected | 表示访问资源时检测到循环 |
511 | Network Authentication Required | 表示代理服务器发现客户端需要进行身份验证才能获得网络访问权限 |
向前端返回详细的错误信息
当错误发生时,除了需要返回相应的状态码之外,还需要返回详情的错误信息。因为状态码只是通用的描述错误的类别,一般无法表示实际发生的具体错误信息。
比如说400
状态码,只是知道前端请求发生了错误,至于如何去修改,仅凭这个是没有办法找到bug
的。
通常来说:返回错误信息的方法有两种:
- 将信息放入
HTTP
响应头 - 将信息通过
HTTP
响应体返回
1、通过自定义头部,将详细的错误信息放入响应头中
X-ERROR-CODE: 2020
X-ERROR-MESSAGE: Bad authentication token
X-ERROR-INFO: http://api.example.com/v1/authentication
2、将错误信息放入响应体中
{
"error": {
"code": 2020,
"message": "Bad authentication token",
"info": "http://api.example.com/v1/authentication"
}
}
从前端的角度来考虑,通过响应体返回会更加容易处理。
这里的错误代码的命名方式,按照后端自己的要求编写即可。
通常情况下,会要求接口的错误信息越详细越好,但这也不是一定的,也会有特殊情况。
一般而言,前端会将后端接口的错误信息原封不动的显示出来,因为前端很难去判断是否有涉密信息或者让用户难堪的信息。比如说A用户屏蔽了B用户,当B用户想看A用户的详情时,如果正确的返回:“A用户已屏蔽B用户,无法获取”的话,会让双方都难堪。这时就需要返回模棱两可的信息。这个就需要后端开发们自己去领悟。
针对默认返回与API
维护
某些接口在发生错误时会将HTML
返回。特别是发生404、503等错误时,这种情况就比较常见。当发生这些错误时,用于构建API
的Web
服务器或者app
框架会直接返回出错信息,默认情况下大都是HTML
。
但虽说是发生了错误,前端依然在访问中,所以仍然期待服务器返回约定好的格式,比如JSON
。尤其在通过Accept
请求头部或扩展名等指定了接收格式时。当然可以让前端去检查Content_Type
头部,进行相应的处理。但如果前端处理的不好或者没有处理,可能会导致app
崩溃。
尤其是公共api
,不能期望所有的使用者都严格遵循规范来处理好,这种api
算不上了好的api
。
关于API
的维护,正常来说,要避免不得不停止API
的发生。但特殊的时候也会不得不停止API
进行维护工作,这种情况需要返回503
状态码来告知前端当前API
已经停止工作。另外,因为这种停止操作不是意外而是有计划进行的,所以要有API
何时重启的时间信息,将其发送给前端。
不仅要预备好用于定期维护的状态码和出错信息的返回,还要使用Retry-After
头部来告诉前端维护结束的时间。从SEO
的角度来看,这个方式对普通的web
站点的维护也同样适用,也是Google
推荐的方法。
Retry-After
的值可以是某个具体的日期或从当前时刻算起至可正常访问为止所需的秒数。
503 Service Temporarily Unavailable
Retry-After: Mon, 9 Sep 2020 20:00:00 GMT
从前端实现的角度来看,在返回503错误时,是期待前端能识别出API
地方Retry-After
值指定的时间等待,然后在API
重启的时候再次访问。
虽然这些处理都取决于前端的具体实现,后端无法对此进行控制,但依然要尽可能地返回详细的信息,方便前端处理并提升用户体验。
总结
主要是三个痛点:
- 必须选择合适的
HTTP
状态码 - 向前端返回详细的错误信息
- 针对默认返回与
API
维护