背景
我在掘金看了很多对 Axios 的封装,各有各的封装目的,今天我来讲下我遇到的一些痛点,及我是怎么解决的。
这些年对接了很多个后端及不同的后端接口,返回的格式各式各样,不过基本上可以归纳为对返回的数据进行了业务封装,HTTP 统一响应 200,然后在响应体里面添加业务代码。但大部分的业务代码就 2 个:成功(0)、失败(1)
比如这样的:
const successResponse = {
status: 200,
statusText: 'ok',
data: {
code: 0,
message: '成功',
result: { /* 真正的数据 */ }
}
}
const errorResponse = {
status: 200,
statusText: 'ok',
data: {
code: 1,
message: '出错了'
}
}
还有比较离谱的,比如响应码还返回了其他比如 500,400 等,导致不仅要判断响应码,还要判断业务码。
所以呢,很多开发人员会编写如下的代码,明明响应成功了还要判断是否有业务错误,并且在没有封装的情况下,需要在各个业务都重复写相同的代码
someServiceApi()
.then((res) => {
if (res.code === 0) {
console.log(res.result)
} else {
console.log(res.message || '出错了')
}
}, (e) => {
console.log(e.message || '出错了')
})
所以我希望能编写 Axios 的拦截器来处理以下问题:
- 后端接口数据的多重封装
- 后端接口的响应错误的不同形式
- 前端有请求处理没有提取重复的逻辑
希望在业务页面能做到:
- 只需调用接口方法
- 只需获取数据
- 不关心是否出错
- 不关心是否需要弹出错误信息
解决方案
下面是我的方案简述,有兴趣的可以直接看源码:enhanced-axios
本解决方案只是针对后端返回的各式各样的数据格式进行规范化,其他的比如 Token 注入与刷新,全局加载,判断跳转登录等功能并未包含,可在该方案的基础上拓展。
另外,由于已经写在了 github 上了,就不再贴代码了,直接上链接,有需要直接点击链接查看。
拦截器一:处理后端接口的封装参数的一致性
这里主要是对后端业务参数的转换,比如 message/msg, code/flag, data/result 后端接口可能出现的业务参数转成标准的 message(响应信息)/code(业务代码)/data(业务数据),这里我把最后转换的数据放在属性 _business 里面,流转到下一个拦截器。
注意,在这个(或者后续的)拦截器里如果是报错信息同样也需要通过 Promise.reject(error) 的方式流转到下一个拦截器。
点击处理后端接口的封装参数的一致性查看代码。
拦截器二:处理401响应
在我的经验里,在用户未登录或登录过期时接口一般会有以下两种情况:
- HTTP 状态码 401
- HTTP 状态码 200/500,但业务数据未认证
这里我需要把第二种情况包装成第一种情况,使得在后续的业务中能够统一判断是否需要去登录。
在代码里借鉴了 Axios 自身的 createError 方式来实现把其他的响应数据转成 401 的响应数据。
点击处理401响应查看代码
拦截器三:处理后端接口的多重封装
这一步主要是解包多重的数据,直接在第一个拦截器返回的 _business 里面读取即可,如果不是成功的响应,则封装成一个 500 的错误响应(同样使用和上述包装 401 响应的方式)
这里也处理了接口报错的信息,可通过配置来弹出或忽略信息提示。
点击处理后端接口的多重封装查看代码
拦截器四:处理后端接口的响应错误
最后的拦截器来处理最终的错误信息,上述的拦截器,只要报错了都会通过 Promise.reject(error) 流转到这里
点击处理后端接口的响应错误查看代码
使用方式
import {
enhancedAxios
} from 'enhanced-axios'
import type { EAxiosInstance } from 'enhanced-axios'
const businessService = axios.create({}) as EAxiosInstance
enhancedAxios(
// axios 实例
businessService,
{
// 成功的业务代码
validBusinessCodes: [200],
// 用户未认证的业务代码
unanthorizedBusinessCodes: [10011039, 10011040, 10011041],
// 业务数据与业务代码的别名, 默认是 code message data
businessAlias: { data: 'result' },
// 执行失败信息的函数,一般是UI组件的函数
warning: (message: string) => notice('danger', message),
// 执行成功信息的函数,一般是UI组件的函数
success: (message: string) => notice('success', message),
// 配置响应成功是否弹出信息
_feedback: false,
// 额外的拦截器配置
interceptors: {
request: [],
response: [],
},
}
)
businessService<MyBusinessDataType>({
url: 'api-url',
method: 'get',
// 如上
_feedback: '成功了',
// 禁止弹出接口返回的 message 及配置的 _feedback
_silent: true
}).then((businessData) => {
// 获取业务数据,类型:MyBusinessDataType
console.log(businessData)
}, (error) => {
// 捕获 http 错误或业务错误
console.log(error)
// 打印由插件默认生成的错误信息
console.log(error._formatMessage())
})
// 如果你的某些请求,想要原始的响应对象,那么创建多一个 axios 实例来请求即可
const originService = axios.create({}) // AxiosInstance
总结
最后,感谢阅读,如果文章有什么错误的地方可以指出,如果文章对您有帮助可以点个赞,给个 star, 再贴一下源码:enhanced-axios,有兴趣的可以直接看。