axios,一次配置,流传千古

前言

最近在和小伙伴一起开发一项目叫LoveMail,是由我之前写给女朋友的哪个“早安邮件”演进而来,立志于为所有小伙伴们提供一个自定义邮件内容每日定时发送邮件的平台,但是很可惜,由于后端开发的小伙伴在准备面试,项目就被搁置了一个多礼拜,但是我们保证,这个项目最后会提供给大家使用。好啦,废话不多说啦,开始今天的主题。

何谓拦截器

我们做一个比喻,axios请求就像停在停车场里面的一辆车,当axios请求发送后,axios就会开出停车场,这时候它首先要经过门禁大叔,这个门禁大叔相当于拦截器,在axios出去之前,大叔就会拦下axios,拷问axios:”你这是要去哪呀?我给你准备点的东西你再走呀。”,然后,当axios获得请求回来后,再次进过门禁,大叔又出来拷问:”你带了哪些东西回来呀,是不是路上有点什么错误呀,balabala…”。拦截器就是大叔,在axios发送请求和接收到请求后,拦截器都会插一手,我们用下面代码来表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const instance = axios.create()
// 发送请求时拦截
instance.interceptors.request.use(
req => {
// something
return req
},
err => {
throw new Error('xxx')
}
)
// 接收请求时拦截
instance.interceptors.response.use(
res => {
return res
},
err => {
throw new Error('xxx')
}
)

这就是axios提供的拦截器,那么它有什么用呢?我将从request和response两个方便举例子。

正确打开方式

request拦截

我们举个例子:假如你发送请求的时候,这个请求是要想服务器发送token验证的,那么我们每次发送请求是不是样像下面一样:

1
2
3
4
5
6
7
8
axios({
method:'get',
url:"/weapp/goods/list",
params:{},
headers:{
Authorization:getCookie('token')
}
})

如果整个项目中获取一次数据还好,如果多处需要获取数据呢?那么就每次都要带上这个Authorization,是不是很麻烦,作为一个上进的程序员,是拒绝写重复代码的,这个时候我们便可以在拦截器中,通过判断特定的请求路径,给请求自动加上token。
如下:

interceptors.request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
instance.interceptors.request.use(
req => {
const token = getCookie('token')
// 公共请求API,请求头不带有Authorization
const publicUrl = ["/signup", "/signin", "/email/validate", "/user/reset", "/temp/all" ]
const url = req.url
// 其他需要Authorization的请求
if (publicUrl.indexOf(url) === -1) {
req.headers.Authorization = token
if (!token){
history.push('/login') // 当cookie中存储的token过期后自动跳转到登录页
}
}
return req
},
err => {
throw new Error('发起请求出错')
}
)

这里,我们通过验证publicUrl,来确定哪个请求需要带上token,假设token不在或者过期,那么这个时候就会跳转到login页面,是不是惊艳到了你,是不是也想去试试,一劳永逸,妈妈再也不用担心我写重复的代码了,不仅如此,假如token的位置发生变化,也不用在每个axios中去修改,只要改一次拦截器中的代码即可。说实话,这个拦截器当时真是一语惊醒梦中人…

response拦截器

拦截器的功能远远不止带上个token和跳转下登录也这么简单,我们再次设想,假设我们请求到返回了错误,比如401验证失败、500服务器错误、超时请求、用户网络不好,这么多可预见性的错误,那么我们在每个请求中都来处理嘛?又是重复的代码,拒绝!分享一下我的错误请求拦截代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
instance.interceptors.response.use(
res => {
return res
},
err => {
// 本地环境错误
if (err.message === "Network Error") {
throw new Error( '网络出错,请稍后再试!')
} else if (err.message.indexOf('time')!==-1) {
throw new Error( '请求超时,请稍后再试!')
} else if (err.response.status===401) {
history.push('/login') // 当cookie中存储的token过期后自动跳转到登录页
} else if (err.response.status===500) {
throw new Error( '服务器出错!')
} else {
throw err // 非本地环境错误
}
}
)

这里,我们通过获得错误信息,来判断到底发生什么错误,相应做出统一,页面的每个只需要关心拦截器传过来的错误信息即可,这样便是response拦截器的使用。

在React中使用

最后贴一段完整的代码,这是我在React比较常用的设置,当然你也可以根据自己的业务需求,做相应的调整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import axios from 'axios'

import { getCookie } from "../config/token" //用cookie存放token
import history from "../config/history" // 路由跳转

const instance = axios.create({
timeout: 5000, //超时时间
baseURL: 'https://api.lovemail.site/v1'
})
// request拦截
instance.interceptors.request.use(
req => {
const token = getCookie('token')
// 公共请求API,请求头不带有Authorization
const publicUrl = ["/signup", "/signin", "/email/validate", "/user/reset", "/temp/all" ]
const url = req.url
// 其他需要Authorization的请求
if (publicUrl.indexOf(url) === -1) {
req.headers.Authorization = token
if (!token){
history.push('/login') // 当cookie中存储的token过期后自动跳转到登录页
}
}
return req
},
err => {
throw new Error('发起请求出错')
}
)
//response拦截
instance.interceptors.response.use(
res => {
return res
},
err => {
// 本地环境错误
if (err.message === "Network Error") {
throw new Error( '网络出错,请稍后再试!')
} else if (err.message.indexOf('time')!==-1) {
throw new Error( '请求超时,请稍后再试!')
} else if (err.response.status===401) {
history.push('/login') // 当cookie中存储的token过期后自动跳转到登录页
} else if (err.response.status===500) {
throw new Error( '服务器出错!')
} else {
throw err // 非本地环境错误
}
}
)

export default instance

总结

这里分享了我自己的封装经验,小生才疏学浅,这或许并不是完美的封装,甚至存在未知的错误,这里只是总结一下我个人平常的开发经验,欢迎大家指正!