封装axios

最近做项目中,我尝试对axios做封装,以简化操作逻辑。所以这篇主要整理一下在项目中封装axios。

axios本身有create方法,可以处理各种请求,有拦截器,有cancelToken,可以根据请求的不同做不同的处理。封装的目的,就是让请求独立,的拦截可以分类,同时需要使用cancelToken的请求可以很方便的使用,把axios封装到一个类中,把axios的功能集中起来,调用的时候只要创建一个实例就可以了。

因此,这里封装就声明了一个Ajaxquest类,引入的时候其实是引入一个Ajaxquest实例。

1
2
3
4
5
class Ajaxquest {
// ...
}

export default new Ajaxquest()

初始化

一般我们在使用axios的时候,有一些属性是可以设置固定的,比如timeout,baseURL等等,这里我把这些属性放在constructor中来初始化化:

1
2
3
4
5
6
constructor () {
this.baseURL = baseUrl
this.withCredentials = true
this.timeout = 60000
this.cancelToken = null // 关于cancelToken后面会单独整理
}

request

这个Ajaxquest类在外部使用的时候是直接使用它的实例的,所以实际是调用实例的方法来使用,而且一般我们调用axios都是设置一些配置,所以我们需要有一个request的方法,这个方法支持传入axios的配置,然后在方法里面处理请求跟拦截。

axios的请求方法比较多,比如axios.create(options)axios.get(options)等。这里我们直接使用axios.create(options),因为它的灵活性更高,可以配置methods:

1
2
3
4
5
6
7
8
9
10
request (config) {
// ...
const service = axios.create({
baseURL: this.baseURL,
withCredentials: this.withCredentials,
timeout: this.timeout
})
// ...
return service(config)
}

return service(config)之前,我们还要处理一步拦截,因为很多时候,一些请求是需要在头部添加一些信息的,而这些信息有些是通用的,有些是个别请求要求携带的,所以可以通过config传进来,然后再传到拦截器中处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
request (config) {
// ...
const service = axios.create({
baseURL: this.baseURL,
withCredentials: this.withCredentials,
timeout: this.timeout
})
service.interceptors.request.use(config => this._interceptorsRequest(config), error => {
console.log('service error: ', error)
// Do something with request error
return Promise.reject(error)
})
service.interceptors.response.use(response => this._interceptorsResponse(response), error => this._interceptorsReject(error))
return service(config)
}

这里面拦截器中的callback,除了请求发送前拦截错误的callback,其他都写成内部方法。这些callback的作用是对所有请求的共性拦截处理。

拦截

拦截器有请求前的拦截跟请求完毕后的拦截,每个拦截都会处理两个callback,因为请求前报错的处理比较少,只是打印报错信息跟返回Promise reject,所以只简单写了callback函数,没有写成内部方法。

请求前的拦截一般是为了配置头部信息,不在request方法的axios.create()中处理只是为了分开处理,是可以放在一起处理的。一些请求不需要配置头部信息,直接过滤掉,然后定义一些配置头部字段的,判断请求配置中是否带有这些属性,有则配置对应的头部信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
_interceptorsRequest (config) {
//当url为/admin/Image ,/admin/File, 不设置X-token请求头,图片和文件上传接口特殊要求
if (/*判断哪些请求不需要配置头部信息*/) {
return config
}

// 自定义配置头部信息的属性,判断带有这些属性,就配置
if (config.areaID) {
config.headers['x-area-id'] = config.areaID
delete config.areaID
}

console.log('service config: ', config)
return config
}

请求完成之后,也做拦截,把请求成功跟失败后的共性处理分别独立放到一个callback中。

请求成功之后,一些全局性的信息就可以在拦截器中处理。比如用户的信息更新,就可以在拦截器中直接处理;

1
2
3
4
5
6
_interceptorsResponse (response) {
console.log('response success: ', response)
const config = response.config
// 处理全局信息
return response
}

如果请求失败了,拦截就可以做一些请求失败的报错信息打印。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 这里用了vant的Toast组件
_interceptorsReject (error) {
console.log('response fail response: ', error.response)

if (error.message === 'cancel a request') return Promise.reject(error)

// 打印错误信息
let errorMessage = '服务器繁忙,请刷新页面重试!'
if (error.response && error.response.headers['x-info'] !== undefined && error.response.headers['x-info'] !== '') {
errorMessage = decodeURIComponent(error.response.headers['x-info'])
} else if (error.message) {
errorMessage = error.message.toLowerCase()
if (errorMessage.indexOf('timeout') > -1) {
errorMessage = '服务器长时间未响应,请刷新页面重试!'
} else if (errorMessage.indexOf('abort') > -1) {
errorMessage = '服务器连接中断 abort!'
}
}
Toast.fail(errorMessage)

return Promise.reject(error)
}

cancelToken

axios提供了cancelToken来处理取消请求。这里我们也来实现cancelToken的封装。

因为我们导出的是实例,写请求方法的时候只是调用了request()方法,所以需要提供另一个方法来处理取消请求。另外,因为cancelToken的处理是需要在请求的时候先创建一个cancel token,所以就是说需要在调用request()方法时提出需要创建cancel token,所以在request()方法需要增加一个逻辑:

1
2
3
4
5
6
7
request (config) {
// ...
if (config.cancelToken) {
this.cancelToken = axios.CancelToken.source()
}
// 接着再处理service跟拦截器
}

这样外部传入一个cancelToken的布尔值,方法内部判断是否为真,是则创建cancelToken。

使用的时候,当需要取消请求,就可以直接调用abort()方法:

1
2
3
4
5
6
abort (notice = 'cancel a request') {
console.log('abort')
if (this.cancelToken) {
this.cancelToken.cancel(notice)
}
}

最后

这个封装其实只是一个结构的封装,内部的配置可以根据不同的项目,不同的请求分类去做差异化的处理。封装的功能也主要是一些常用的请求处理,有其他需求还可以在这个基础上继续封装。