axios取消重复请求与更新token并续订上次请求
一、问题引入
当用户发起一个请求时,判断token是否已过期,若已过期则先调refreshToken接口,拿到新的token后再继续执行之前的请求。
难点:当同时发起多个请求,token 过期会调用多次更新 token 接口;此时刷新token的接口还没返回,此时其他请求该如何处理,在刷新token接口返回后才能续订请求
二、取消重复请求
针对同时发起多个请求,token 过期会调用多次更新 token 接口,这里只做了取消重复请求操作,未能续订之前请求
维护一个 请求列表 reqList
在 axios 请求拦截器中判断请求是否已经在 reqList 中存在,存在则调用 axios.CancelToken 取消请求
// 正在进行中的请求列表
let reqList = [] /**
* 阻止重复请求
* @param {array} reqList - 请求缓存列表
* @param {string} url - 当前请求地址
* @param {function} cancel - 请求中断函数
* @param {string} errorMessage - 请求中断时需要显示的错误信息
*/
const stopRepeatRequest = function (reqList, url, cancel, errorMessage) {
const errorMsg = errorMessage || ''
for (let i = 0; i < reqList.length; i++) {
if (reqList[i] === url) {
cancel(errorMsg)
return
}
}
reqList.push(url)
} /**
* 允许某个请求可以继续进行
* @param {array} reqList 全部请求列表
* @param {string} url 请求地址
*/
const allowRequest = function (reqList, url) {
for (let i = 0; i < reqList.length; i++) {
if (reqList[i] === url) {
reqList.splice(i, 1)
break
}
}
} const service = axios.create() // 请求拦截器
service.interceptors.request.use(
config => {
let cancel
// 设置cancelToken对象
config.cancelToken = new axios.CancelToken(function(c) {
cancel = c
})
// 阻止重复请求。当上个请求未完成时,相同的请求不会进行
stopRepeatRequest(reqList, config.url, cancel, `${config.url} 请求被中断`)
return config
},
err => Promise.reject(err)
) // 响应拦截器
service.interceptors.response.use(
response => {
// 增加延迟,相同请求不得在短时间内重复发送
setTimeout(() => {
allowRequest(reqList, response.config.url)
}, 1000)
// ...请求成功后的后续操作
// successHandler(response)
},
error => {
if (axios.isCancel(thrown)) {
console.log(thrown.message);
} else {
// 增加延迟,相同请求不得在短时间内重复发送
setTimeout(() => {
allowRequest(reqList, error.config.url)
}, 1000)
}
// ...请求失败后的后续操作
// errorHandler(error)
}
)
三、更新token并续订上次请求
此方法可用于多个请求token失效,进行多次更新 token 请求操作时,取消其他更新 token 请求,并在完成更新后续订上次多个请求
// 是否正在刷新的标记
let isRefreshing = false
// 重试队列,每一项将是一个待执行的函数形式
let retryRequests = [] // 请求拦截
service.interceptors.request.use(
config => {
const token = localStorage.getItem('access_token')
if (token && !config.url.includes('/oauth/token')) {
config.headers.Authorization = 'Bearer ' + token;
}
if (config.method === 'post' || config.method === 'put') {
config.data = qs.stringify(config.data);
}
return config;
},
err => {
return Promise.reject(err);
}
); // 响应拦截
service.interceptors.response.use(
async response => {
if (response.status === 200) {
if (response.data && response.data.code == 4001) {
const config = response.config
if (!isRefreshing) {
isRefreshing = true
// const refresh_token = localStorage.getItem('refresh_token')
// const token = localStorage.getItem('access_token')
try {
const result = await getToken({
grant_type: 'password',
client_id: 'client-app',
client_secret: '123',
username: 'sunhj',
password: '123'
})
localStorage.setItem('access_token', result.token || '')
// localStorage.setItem('refresh_token', res.refreshToken || '')
const token = localStorage.getItem('access_token')
config.headers['Authorization'] = 'Bearer ' + token
// 已经刷新了token,将所有队列中的请求进行重试
retryRequests.forEach(cb => cb('Bearer ' + token))
// 重试完清空这个队列
retryRequests = []
// 这边不需要baseURL是因为会重新请求url,url中已经包含baseURL的部分了
config.baseURL = proxyHost
if (config.method === 'post' || config.method === 'put') {
config.data = qs.parse(config.data)
}
isRefreshing = false
return service(config)
} catch (error) {
isRefreshing = false
}
} else {
// 正在刷新token,返回一个未执行resolve的promise
return new Promise((resolve) => {
// 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
retryRequests.push((token) => {
config.baseURL = proxyHost
config.headers['Authorization'] = token
if (config.method === 'post' || config.method === 'put') {
config.data = qs.parse(config.data)
}
resolve(service(config))
})
})
}
} else {
// 二进制流文件下载
if (response.headers['content-type'] === "application/octet-stream;charset=UTF-8") {
return response;
} else {
return response.data;
}
}
} else {
Promise.reject();
}
},
err => {
return Promise.reject(err);
}
);
参考:https://blog.csdn.net/weixin_45178716/article/details/103286459
axios取消重复请求与更新token并续订上次请求的更多相关文章
- axios 取消请求
解决思路 在发送第二次请求的时候如果第一次请求还未返回,则取消第一次请求,以保证后发送的请求返回的数据不会被先发送的请求覆盖. axios官方文档取消请求说明 方法一: const CancelTok ...
- 从 axios 源码中了解到的 Promise 链与请求的取消
axios 中一个请求取消的示例: axios 取消请求的示例代码 import React, { useState, useEffect } from "react"; impo ...
- axios构建请求池处理全局loading状态&&axios避免重复请求
很多时候我们能够看到类似进度条一样的东西在页面顶部进行加载,代表页面是否加载完成,或者其他的loading效果,我们当然不可能通过promise.all来讲所有的请求合并到一起然后进行处理,这个时候我 ...
- axios取消接口请求
axios取消请求 这里就是分析一下接口请求需要被取消时的一些操作 因为我是用vue写的项目,所以标配用的是axios,怎么在axios中取消已经发送的请求呢? 1.在这之前我们还是先介绍一下原生js ...
- axios 取消请求的方法
开发中遇到需要取消请求的功能,,点击终止查询可以取消开始查询请求,再次点击开始查询又可以进行查询. 解决方法:axios官方文档上的CancelToken,一开始用了这个api后,可以成功取消请求,但 ...
- axios中为所有请求带上Token头
axios中为所有请求带上Token头 https://www.imooc.com/article/27751
- Axios 取消 Ajax 请求
Axios 取消 Ajax 请求 Axios XMLHttpRequest https://caniuse.com/?search=XMLHttpRequest https://developer.m ...
- 学习AJAX必知必会(3)~自动重启工具nodemon、缓存问题、请求超时和网络异常、取消重复请求
1.nodemon 自动重启工具(自动重启基于nodejs开发的服务端应用) ■ nodemon 是一个工具,通过在检测到目录中的文件更改时自动重新启动node应用程序来帮助开发node.js. // ...
- axios 访问和返回拦截,token处理,返回异常统一处理
在axios文件夹中,index.js添加拦截 访问拦截: import store from '../store' axios.interceptors.request.use( config =& ...
- axios interceptors 拦截 , 页面跳转, token 验证 Vue+axios实现登陆拦截,axios封装(报错,鉴权,跳转,拦截,提示)
Vue+axios实现登陆拦截,axios封装(报错,鉴权,跳转,拦截,提示) :https://blog.csdn.net/H1069495874/article/details/80057107 ...
随机推荐
- k8s如何配置secret保存harbor仓库账号密码、pod中怎么使用harbor仓库镜像
转载: https://blog.csdn.net/MssGuo/article/details/127312239
- windows10本地联调zk环境报异常SASL config status: Will not attempt to authenticate using SASL (unknown error)
感谢原文:https://blog.csdn.net/qq_43639296/article/details/123282280 SASL config status: Will not attemp ...
- @Configuration 配置类打断点后,一启动项目读取到该配置类的话就会进断点
@Configuration 配置类的话,打断点的时候,一启动项目就会读取配置信息,然后你在@Configuration 配置的类中打断点的话,一启动项目就会读取配置类,然后就会进断点,跟你平常的co ...
- <string> <cstring> <string.h>
<string>是C++标准库头文件,包含了拟容器class std::string的声明(不过class string事实上只是basic_string<char>的type ...
- C++ 11 数字转字符串新功能
// 头文件 <string>string to_string (int val);string to_string (long val);string to_string (long l ...
- kubectl --v日志级别
Kubectl 日志输出详细程度是通过 -v 或者 --v 来控制的,参数后跟了一个数字表示日志的级别.Kubernetes 通用的日志习惯和相关的日志级别在 这里 有相应的描述. 详细程度 描述-- ...
- system verilog与C语言的接口(包含使用方法以及实例)
资料来源 (1) sv绿皮书; (2) vcs user guide; (3) https://www.cnblogs.com/studyforever/p/5169243.html (4) syst ...
- JS中call、apply、bind
call就是挨个传值,apply传一个数组,bind也是挨个传值 call() 和 apply() 会执行这个函数bind并不会而是将绑定好的this重新返回一个新函数
- 基于ipset的dns代理
###基于源IP的dns白名单 sleep 60s/etc/init.d/firewall restart ###创建ipset集合 命名为src_dns_whitelist#清空原有命名为src_d ...
- 标量子查询加聚合函数sql改写一
标量子查询的语句: select /*+ GATHER_PLAN_STATISTICS dwtest */ empno, (select count(*) from DEPT1 b where b.i ...