axios
axios拦截器
-
拦截器是什么?
- 以我的理解,拦截器就是一个回调函数,里面包含了一些逻辑处理的代码,它分为请求拦截器,其在请求发送之前执行,处理一些请求发送之前的逻辑,响应拦截器,其在响应拿到之后,回调函数执行之前执行,处理一些拿到响应数据的逻辑。
为了方便演示,我使用koa写了一个简易的接口,用于处理请求返回数据
const Koa = require('koa');
const router = require('koa-router')();
const jsonp = require('koa-jsonp');
const cors = require('koa-cors');
var app = new Koa();
app.use(cors());
app.use(jsonp());
async function delay(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, time)
})
}
router.get('/products1', async (ctx) => {
await delay(1000 + Math.random() * 1000)
ctx.body = [{
id: 1,
name: 'products1.1'
},
{
id: 2,
name: 'products1.2'
},
{
id: 3,
name: 'products1.3'
}
];
});
router.get('/products2', async (ctx) => {
await delay(1000 + Math.random() * 1000)
ctx.body = [{
id: 1,
name: 'products2.1'
},
{
id: 2,
name: 'products2.2'
},
{
id: 3,
name: 'products2.3'
}
];
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(4000);
console.log('Server running at http://localhost:4000');
演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
<script>
//添加请求拦截器(回调函数)
axios.interceptors.request.use(
config => {
console.log('request interceptor1 onResolved()')
return config
},
error => {
console.log('request interceptor1 onRejected()')
return Promise.reject(error)
}
)
axios.interceptors.request.use(
config => {
console.log('request interceptor2 onResolved()')
return config
},
error => {
console.log('request interceptor2 onRejected()')
return Promise.reject(error)
}
)
//添加响应拦截器
axios.interceptors.response.use(
response => {
console.log('response interceptor1 onResolved()')
return response
},
error => {
console.log('response interceptor1 onRejected()')
return Promise.reject(error)
}
)
axios.interceptors.response.use(
response => {
console.log('response interceptor2 onResolved()')
return response
},
error => {
console.log('response interceptor2 onRejected()')
return Promise.reject(error)
}
)
axios.get('http://localhost:3000/posts')
.then(response => {
console.log('data', response.data);
})
.catch(error => {
console.log('error', error.message)
})
</script>
</body>
</html>
-
执行顺序
-
请求拦截器2 ------请求拦截器1-------发送ajax请求-------响应拦截器1-------响应拦截器2------响应回调
此流程是通过Promise串联起来的,请求拦截器传递的是config,响应拦截器传递的是response
axios取消发送请求
取消发送请求,顾名思义就是取消正在发送的ajax请求,这在业务场景中是会遇到的
基本流程
- 配置cancleToken对象
- 缓存用于取消的cancel函数
- 在后面特定时机调用cancel取消请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>取消请求</title>
</head>
<body>
<div>
<button onclick="getOne()">获取商品列表1</button>
<button onclick="getTwo()">获取商品列表2</button>
<button onclick="cancelReq()">取消请求</button>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
<script>
let cancel //用于保存取消请求的函数
function getOne() {
axios({
url: 'http://localhost:4000/products1',
cancelToken: new axios.CancelToken((c) => { //c是用于取消当前请求的函数
//保存取消函数,用于之后可能需要取消当前请求
cancel = c
})
})
.then(
//不管成功还是失败,都应该没有机会取消了
response => {
cancel = null
console.log('请求1成功了', response.data)
},
error => {
cancel = null
console.log('请求1失败了', error.message)
}
)
}
function getTwo() {
axios({
url: 'http://localhost:4000/products2',
})
.then(
response => {
console.log('请求2成功了', response.data)
},
error => {
console.log('请求2失败了', error.message)
}
)
}
function cancelReq() {
//alert('取消请求')
//执行取消请求的函数
if (typeof cancel === 'function') {
cancel('强制取消请求')
} else {
console.log('没有可取消的请求')
}
}
</script>
</body>
</html>
实例演示
上面的那个例子实现了点击按钮取消发送请求,但是我们会遇到一个情况,用户点击获取列表1的数据之后又点击获取列表2的数据,若列表1和列表2与前台同一位置显示,那么先前请求的列表1的数据的请求是否就可以直接中断而光去请求列表2的数据,这就需要实现在请求一个接口前,取消前面一个未完成的请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>取消请求</title>
</head>
<body>
<div>
<button onclick="getOne()">获取商品列表1</button>
<button onclick="getTwo()">获取商品列表2</button>
<button onclick="cancelReq()">取消请求</button>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
<script>
let cancel
function getOne() {
if (typeof cancel === 'function') {
cancel('强制取消')
}
axios({
url: 'http://localhost:4000/products1',
cancelToken: new axios.CancelToken(c => {
cancel = c
})
}).then(
response => {
cancel = null
console.log("请求1成功了");
},
error => {
if (axios.isCancel(error)) {
console.log("取消1导致的错误,不应将cancel置空");
} else {
cancel = null
console.log("请求1失败了");
}
}
)
}
function getTwo() {
if (typeof cancel === 'function') {
cancel('强制取消')
}
axios({
url: 'http://localhost:4000/products2',
cancelToken: new axios.CancelToken(c => {
cancel = c
})
}).then(
response => {
cancel = null
console.log("请求2成功了");
},
error => {
if (axios.isCancel(error)) {
console.log("取消2导致的错误,不应将cancel置空");
} else {
cancel = null
console.log("请求2失败了");
}
}
)
}
function cancelReq() {
if (cancel) {
cancel('强制取消')
} else {
console.log("没有可以取消的请求");
}
}
</script>
</body>
</html>
因为所有请求都是用的一个cancel,所以cancel为function,则证明前一个请求并没有完成,因为完成请求之后cancel会被置为null
注意这里要写成上面这样,而不是下面
error => {
if (axios.isCancel(error)) {
console.log("取消1导致的错误,不应将cancel置空");
} else {
cancel = null
console.log("请求1失败了");
}
}
error => {
cancel = null
console.log("请求1失败了");
}
因为error是异步执行的
比如:点击发送第一次 ---- 点击发送第二次,这时第一次的请求被中断,但是因为error是异步的,所以error的代码并没有马上执行-----点击发送第三次,这是error的代码执行了,cancel被置为null,但此时第二次请求仍在请求中,进入第三次的判断中,cancle不为function,所以无法中断第二次发送的请求,我们使用axios中的isCancel函数可以判断错误是否是因为取消发送请求产生的,若是因为取消发送请求产生的,我们不将cancel置为空就可以解决这个问题
这样虽然完成了要求,但是我们发现重复的地方很多,若是请求多了之后,难道每一个都要再写一遍?有人会想到封装成函数,这里封装成函数并没有改变多少,还是每一个都要调用
这就要使用到我上面提到的拦截器了,拦截器可以对所用的请求(响应)做处理,既然所有的请求(响应)里面都有这样相似的代码,用拦截器在请求发送之前(响应得到之后)一起处理,岂不美哉,这就是拦截器的好处,下面我们来看
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>取消请求</title>
</head>
<body>
<div>
<button onclick="getOne()">获取商品列表1</button>
<button onclick="getTwo()">获取商品列表2</button>
<button onclick="cancelReq()">取消请求</button>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
<script>
//添加请求拦截器
axios.interceptors.request.use((config) => {
//1.在准备发请求前,取消上一个未完成的请求
if (typeof cancel === 'function') {
cancel('强制取消')
}
//2.添加一个cancelToken的配置
config.cancelToken = new axios.CancelToken(c => {
cancel = c
})
return config
})
//添加一个响应拦截器
axios.interceptors.response.use(
(response) => {
cancel = null
return response
},
error => {
if (axios.isCancel(error)) {
console.log("取消导致的错误,不应将cancel置空");
//中断Promise链
return new Promise(() => {})
} else {
cancel = null
//非取消导致的错误,将错误向下传递 让请求自己处理
return Promise.reject(error)
//也可以抛出错误 throw error
}
}
)
let cancel
function getOne() {
axios({
url: 'http://localhost:4000/products1'
}).then(
response => {
console.log("请求1成功了");
},
error => {
//只用处理请求失败的情况,取消请求导致的错误不用处理
console.log("请求1失败了");
}
)
}
function getTwo() {
axios({
url: 'http://localhost:4000/products2'
}).then(
response => {
console.log("请求2成功了");
},
error => {
console.log("请求2失败了");
}
)
}
function cancelReq() {
if (cancel) {
cancel('强制取消')
} else {
console.log("没有可以取消的请求");
}
}
</script>
</body>
</html>