Appearance
API 响应规范
本文以问答形式介绍。
HTTP 状态码还是code
表示状态?
不用争,不用犟,2 个流派都可以。code
流派的做法是,HTTP 状态码一律200
,然后code
只要不是成功的那个值(比如200
或0
什么的),就默认表示出错状态。所以code
流派其实也能用。
响应错误包括几类?
运行时级别错误 | 框架级别错误 | 业务级别错误 | |
---|---|---|---|
原因 | 后端程序员造成的错误 | 前端程序员造成的错误 | 不符合用户预期 |
举例 | 最著名的是空指针异常 | 前端少传参数、数据类型错误等 | 登录过期、删除失败等 |
HTTP 状态码 | 500 等,由 Spring Boot 捕获并返回 | 同左 | 某些错误也可被 Spring Boot 捕获,其他错误由程序员自己决定 |
前端是否应考虑运行时级别错误?
不应考虑。
可能你会说,你见过一些网站抛出过运行时错误。没错,我连 Nginx 报错都见过,我也见过整屏的 Java 报错,但这些网站都是渣滓网站,如果比烂而不是比优,那么本文也没有讨论意义。
我们应当考虑如何避免一切运行时错误,而不是考虑前端如何优雅展示运行时错误。
前端是否应考虑框架级别错误?
Spring Boot 是框架,抛出的是框架级别错误,也就是说大部分属于前后端 Bug 和故障,既然是 Bug 就要早早解决,而不是总想着抛给前端让前端去提示。在解决了这些 Bug 之后,Spring Boot 依然可能返回的非200
状态码应当只有401
。
后端是否应细分 HTTP 状态码?
需要细分比如201
、403
、404
、502
吗?除非你的项目是一个纯资源型网站,那么你可以去细分,否则不用。99.99%的项目都不用去细分。
你只需要使用200
、401
、500
这 3 个状态码就足够,你以为的那些七七八八的状态,都可以用这 3 个状态码概括。
前端业务如何区分200
/401
/500
?
200
:在用户预期内的响应,一律用200
状态码。包括空列表也是200
状态码。500
:在用户预期外的响应,一律用500
状态码。包括业务逻辑导致的预期外,比如无权限访问、资源不存在、创建失败、修改失败、删除失败等情况。401
:用户登录态过期的情况,使用401
状态码。
业务是否应细分错误code
?
比如微信小程序云开发的ErrCode 列表有通用的几十个 ErrCode,每个 API 还有自己的几十个 ErrCode,那么我们小公司小团队是否也应该这样做?
这就引出一个细节:面向开发者的 API 和面向用户的 API 是两码事。
微信小程序云开发的 API 是面向开发者的,这意味着 API 不是本公司在维护,没办法联调,只能单向调试,为方便调试,ErrCode 详尽为宜。但是面向用户的 API 意味着前后端都是本公司在维护,可以联调,所以并不需要如此详尽的 ErrCode。
那么是不是code
可以统一成500
呢?也不是。事实上:
微信小程序云开发的通用 ErrCode,相当于 Spring Boot 的框架级别报错
微信小程序云开发的单个 API 的 ErrCode,相当于 Spring Boot 的业务级别报错
所以说,我们的项目的通用 ErrCode 直接依赖 Spring Boot 返给前端,而后端程序员只需要抛出业务级别错误就行了,同时,还是那句话,没必要搞那么多code
。
总结 HTTP 状态码和code
的使用
本节的讨论是基于上文的精简状态码的前提下讨论。加粗字体的是两个流派的区别。
利用 HTTP 状态码流派
状态码 | 含义 | code 是否有意义 |
---|---|---|
200 | 成功 | 无意义 |
500 | 可能是业务级别报错,全部设为500 | 有意义,应细分 |
500 | 可能是 Spring Boot 的框架级别报错 | 无意义 |
其他值 | Spring Boot 的框架级别报错 | 无意义 |
不用 HTTP 状态码流派
状态码 | 含义 | code 是否有意义 |
---|---|---|
200 | 可能是成功 | 有意义,可设为200 或0 |
200 | 可能是业务级别报错 | 有意义,可设为除了200 或0 以外的值 |
500 | 可能是 Spring Boot 的框架级别报错 | 无意义 |
其他值 | Spring Boot 的框架级别报错 | 无意义 |
公司如何内定成功和失败code
?
也有很多流派,哪个流派都行,数字而已,不重要。举两个例子:
表示成功 | 表示失败 | |
---|---|---|
例一 | 0 | -1 、-2 等负值 |
例二 | 20000 | 50001 、50002 等大于 50000 的数值 |
axios 拦截器怎么写?
利用 HTTP 状态码流派
伪代码如下:
js
service.interceptors.response.use(
(response) => {
const code = response.status;
if (code === 200) {
// 下面这个 data 不是响应体里的data,而是整个响应体
return response.data;
} else if (code === 401) {
removeToken();
router.replace({ name: 'Login' });
} else {
return Promise.reject(response.data.code);
}
},
() => {
Message.alert('网络不给力');
}
);
不用 HTTP 状态码流派
伪代码如下:
js
service.interceptors.response.use(
(response) => {
const code = response.data.code;
if (/* code 匹配上成功 code */) {
return response.data;
}
// 你的框架可能是用状态码返回`401`,也可能是用`code`返回,根据情况改
else if (response.status === 401) {
removeToken();
router.replace({ name: 'Login' });
} else {
return Promise.reject(code);
}
},
() => {
Message.alert('网络不给力');
return Promise.reject('Network Error');
}
);
注意事项
上方代码实际上没有考虑框架级别报错(只考虑了
401
错误),因为生产环境不应当出现框架级别报错,如果出现,要么是 Bug,要么是服务器故障,没必要针对 Bug 写业务逻辑。拦截器的失败分支,即弹窗又返回了
Message.alert('网络不给力')
又return Promise.reject()
,用意是统一弹出“网络不给力”,但有些页面区块需要显示重试按钮,因此需要获取网络不给力的反馈,所以既弹窗又return
。这时.then()
的第二个回调既可能收到错误code
,也可能收到Network Error
,根据这个可以判断是业务错误还是网络错误。拦截器的失败分支也包含网络连接超时的情况,为什么不提示“网络连接超时”呢?因为用户并不懂这是什么意思,也没有指导下一步的作用,所以统称“网络不给力”就够了。
前端应该无脑弹出 msg 字段吗?
显然,不应该。后端返回的msg
应当是简洁又无歧义的文字,甚至可以是英文,即便是中文,前端程序员也不能将前端弹出的内容交给后端程序员去控制,因为后端只负责 API 功能,前端负责 API 呈现。
为何某些一站式框架要这么干?
某些前端一站式框架在 axios 响应拦截器无脑弹出错误消息,这怎么解释?
那些框架是后台管理框架,业务逻辑很“单纯”,弹出之后没有后续逻辑,所以无脑弹出也有一定的道理。
它弹出错误消息之后,继续把错误内容抛给了
.then()
的第 2 个回调,如果真的需要后续业务逻辑,可以继续写逻辑。那些框架是给全栈开发者使用的,开发者后端前端一肩挑,当然可以仔细思索
msg
的内容,写在后端也无妨。
所以后端管理系统如果不是足够复杂的话,可以前端无脑弹msg
内容。
普适的话前端怎么处理msg
?
msg
内容仅供参考,前端弹出的文字还得是前端程序员自己写,然后在.then()
的第 2 个回调里弹出。
js
this.getList(this.params).then(
(response) => {
// 处理成功响应
},
(code) => {
switch (code) {
case -1:
// ...
break;
// ...
case 'Network Error':
// ...
break;
}
}
);
TIP
对于后台管理系统,业务失败后通常没有后续逻辑,即便无网络或连接超时也已经在拦截器做了提示,因此可以直接省掉失败分支。