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 ...
随机推荐
- BUU刷题记录
[GWCTF 2019]mypassword xss+csp 打开页面可以注册登录 登进去提示不是sql注入 然后提示源码 看一下 然后有段后端代码写道了注释里 <!-- if(is_array ...
- 赤菟V307与Matlab的串口通信
赤菟V307与Matlab的串口通信 赤菟V307(CH32V307)是一款RISC-V内核的MCU,搭载的是沁恒自研RISC-V内核青稞V4F,最高主频144MHz,支持单精度浮点运算(FPU). ...
- QE11 / QE51N 界面太小问题
修复后界面是,修复前常规页签中的数据只能显示4行,需要的note是 2639352 , SNOTE 进行打补丁就好 note是 2639352
- acl规则问题
在acl规则中网络地址与广播地址包含在规则范围内
- charls抓包的乱码问题解决
1. 在charls的安装目录下,去修改配置文件的值 ----Charles.ini,内容如下 2. SSL proxy setting 设置如下图 3. 顺便说一下,charls抓取 https的包 ...
- Kotlin属性委托
业务定义 对于属性,我们可以读取(get)和赋值(set),在Java中会定义get和set方法来操作属性,Kotlin的属性建议直接操作,一些业务的要求会对属性有额外的功能需求,在Java中会在ge ...
- ORCAD中,怎么一次性去掉所有元器件下面的下划线呢
选择元器件,右键找到unset单击就可以去掉了
- Java常用数据结构
1.数组 数组(Array) 是一种很常见的数据结构.它由相同类型的元素(element)组成,并且是使用一块连续的内存来存储. 我们直接可以利用元素的索引(index)可以计算出该元素对应的存储地址 ...
- SPI接口(续二)
接下来看SPI接收器数据寄存器RXDAT,下表是它的全部位结构,其地址分别为0x40058014(SPI0).0x4005C014(SPI1). (1)第0到15位(RXDAT)为接收器数据,它包含接 ...
- 【Python】Python多进程练习
1,进程启动 通过Process方法启动,通过下面的代码执行结果,可以发现,开启的进程与WHILE循环语句会同时进行. 为什么呢?因为都是启动了进程在运行程序. from time import sl ...