前言
功能点
此文主要是基于vuecli3项目中axios封装及api管理的实践记录及过程中的踩坑收获,功能基本都是根据工作中需求实现。需求背景是,在同一套请求配置下,实现以下主要功能:
- 自定义请求配置
- 设置全局拦截器
- 响应成功及异常的全局拦截统一处理
- 防止重复请求(取消当前重复的请求)
- 路由切换取消当前所有pending状态的请求(可配置白名单)
- 单独取消发出的某个请求
- api统一管理
axios一些特性
在开始之前,首先明确一些axios的特性,这些特性会影响到某些功能的实现方式:
- 通过
axios.create()
方法创建的实例对象只有常见的数据请求方法,没有取消请求、并发请求等方法。可通过Object.keys()
将所有的key打印出来对比得知。 - axios拦截器是可以累加的,每添加一个拦截器,就会返回一个对应的拦截器id,也就是无法通过新增拦截的方式覆盖或者改变已有拦截器的配置。但可以利用拦截器id通过
axios.interceptors.request.eject(InterceptorId)
方法移除指定拦截器。 - 对于同一个axios对象,如果全局拦截器中设置了
CancelToken
属性,就无法在单独的请求中再通过此属性取消请求。移除全局拦截器可以解决这个问题,但又会有另一个问题,拦截器移除后就永远失效了,影响是全局的。 - axios中以别名的形式(axios.get、axios.post)发请求,不同的请求方式参数的写法是不一样的,主要是put/post/patch三种方法与其他不太一样
自定义请求配置
根目录下新建plugins/axios/index.js
文件,自定义axios的请求配置。
这里process.env.VUE_APP_BASEURL
是一个定义好的变量,值为"/webapi";
设置超时时间timeout
为10s。如下:
import axios from 'axios'
axios.defaults.baseURL = process.env.VUE_APP_BASEURL
axios.defaults.timeout = 10000
axios.defaults.headers['custom-defined-header-key'] = 'custom-defined-header-value'
// 自定义请求头:对所有请求方法生效
axios.defaults.headers.common['common-defined-key-b'] = 'custom value: for all methods'
// 自定义请求头:只对post方法生效
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// 自定义请求头:只对get方法生效
axios.defaults.headers.get['get-custom-key'] = 'custom value: only for get method';
export default axios
"main.js"文件:
import request from '@/plugins/axios/index.js'
Vue.prototype.$request = request
这样在组件内就可以通过this.$request(options)
或者this.$request.get(options)
的方法来请求数据了。
对常见的响应情况统一处理
这里主要是在"响应拦截器"中,对于一些常见的请求状态码和跟后端约定好的请求返回码做统一的前置处理。
新建axios.handleResponse.js
文件,用于处理常见的正常响应:
# axios.handleResponse.js
// 处理响应错误码
export default (response) => {
const status = response.status
// 如果http响应状态码response.status正常,则直接返回数据
if ((status >= 200 && status <= 300) || status === 304) {
return response
}
// status不正常的话,根据与后端约定好的code,做出对应的提示与处理
// 返回一个带有code和message属性的对象
else {
const code = parseInt(response.data && response.data.code)
// msg为服务端返回的错误信息,字段名自定义,此处以msg为例
let message = (response.data || {
}).msg
switch (code) {
case 400:
break
case 4001:
if (process.server) return
message = message || '登录设备数量超出限制'
// store.commit('savehttpResult', { res: response.data })
break
case 403:
message = message || '未登录'
break
case 404:
message = message || '请求地址错误'
break
case 412:
message = message || '未找到有效session'
break
default:
// message = message || err.response.data.msg
break
}
return {
code,
message
}
}
}
新建plugins/axios/axios.handleError.js
文件,用于处理常见的异常响应:
#plugins/axios/axios.handleError.js文件
export default (err) => {
const {
response } = err
if (!response.status) {
err.code = ''
err.message = '有response但没有response.status的情况'
}
err.code = response.status
switch (response.status) {
case 200:
err.message = '错误响应也会有状态码为200的情况'
break
case 400:
err.message = '请求错误(400)'
break
case 401:
err.message = '未授权,请重新登录(401)'
break
case 403:
err.message = '拒绝访问(403)'
break
case 404:
err.message = '请求出错(404)'
break
case 408:
err.message = '请求超时(408)'
break
case 500:
err.message = '服务器错误(500)'
break
case 501:
err.message = '服务未实现(501)'
break
case 502:
err.message = '网络错误(502)'
break
case 503:
err.message = '服务不可用(503)'
break
case 504:
err.message = '网络超时(504)'
break
case 505:
err.message = 'HTTP版本不受支持(505)'
break
default:
err.message = `连接出错,状态码:(${
err.response.status})!`
}
return err
}
plugins/axios/index.js
文件中引入并在拦截器中配置:
- 如果请求被取消,会进入到响应拦截器的第二个参数err处理中
#plugins/axios/index.js文件
import axios from 'axios'
import handleResponse from '@/plugins/axios/axios.handleResponse.js'
import handleError