vue3基于axios配置请求拦截器响应拦截器以及无感刷新token的实现
- 一、下载并引入 axios
- 二、引入 element-plus 消息提示
- 三、配置基础地址
- 四、创建 axios 实例
- 五、配置请求拦截器
- 六、定义是否刷新 token 的变量以及存储请求队列的数组
- 七、配置响应拦截器以及实现无感刷新 token
- 八、导出axios
- 九、封装接口请求
- 十、使用接口请求
- 十一、完整代码
一、下载并引入 axios
npm i axios
创建request=>index.js
import axios from 'axios'
二、引入 element-plus 消息提示
import { ElMessage, ElMessageBox } from 'element-plus'
三、配置基础地址
可参考历史文章vue3项目打包上线后,如何配置api接口地址,省去更换api再次打包
//生产环境下读取 public=>static=>config.js 中的 api 变量(用于上线后动态配置接口地址)
const BASE_URL =
process.env.NODE_ENV === 'development'
? process.env.VUE_APP_API_URL
: window.webConfig.webApiBaseUrl
四、创建 axios 实例
const request = axios.create({
baseURL: BASE_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
五、配置请求拦截器
request.interceptors.request.use((config) => {
config.headers = config.headers || {}, //设置请求头
config.headers['Token'] = localStorage.getItem('Token') || ''
config.headers['Code'] = localStorage.getItem('Code') || ''
return config
})
六、定义是否刷新 token 的变量以及存储请求队列的数组
//是否刷新 token
let isRefreshing = false
//请求队列
let requestList = [] as any[]
七、配置响应拦截器以及实现无感刷新 token
request.interceptors.response.use(
async (res: any) => {
// 路由
const Code: number = res.data.Code || 0const config = res.config//处理token过期==>更新tokenif (Code == -1) {if (!isRefreshing) {isRefreshing = true//id (用于获取token)const appid = process.env.VUE_APP_APPID.replace(/"/g, '')//密钥 (用于获取token)const secret = process.env.VUE_APP_SECRET.replace(/"/g, '')//请求获取token接口return (request({url: `outside/getAccessToken?appid=${appid}&secret=${secret}`,method: 'GET',}).then((res) => {//存token缓存localStorage.setItem('Token', res.data.data)config.headers.token = res.data.datalet response = request(config)// 刷新成功后将请求队列里的请求依次执行requestList.forEach((cb) => cb())requestList = [] // 执行完毕后将请求队列置空return response}).finally(() => {isRefreshing = false}))} else {return new Promise((resolve) => {requestList.push(() => {config.headers.token = localStorage.getItem('Token')let response = request(config)resolve(response)})})}} else if (Code == 0) {//处理code过期==>退出登录 (tokenExpired是防止code过期后,下方提示消息显示多次)let tokenExpired = localStorage.getItem('tokenExpired')if (tokenExpired == 'false' || !tokenExpired) {localStorage.setItem('tokenExpired', 'true')ElMessageBox.confirm('登录已过期,请重新登录!', '提示', {confirmButtonText: '确定',showClose: false,showCancelButton: false,closeOnClickModal: false,type: 'warning',}).then(() => {//清除缓存localStorage.removeItem('Token')localStorage.removeItem('UserGlobalStore')localStorage.removeItem('Code')// 重新加载页面(清除路由缓存)window.location.reload()// 在页面加载完成后跳转到登录页面window.onload = function () {window.location.href = '/login'}})} else {//清除缓存localStorage.removeItem('Token')localStorage.removeItem('UserGlobalStore')localStorage.removeItem('Code')// 重新加载页面(清除路由缓存)window.location.reload()// 在页面加载完成后跳转到登录页面window.onload = function () {window.location.href = '/login'}}} else {return Promise.resolve(res)}},
(err) => {
//接口出错
ElMessage.error(err.message)
return Promise.reject(err)
}
)
八、导出axios
export default request
九、封装接口请求
import request from '@/request/index'export function getYourApi(data: Object) {
return request({
url: 'yourApi',
method: 'GET',
params: data,
})
}
十、使用接口请求
import { getYourApi } from '@/api/yourApi'
getYourApi({}).then((res) => {
...
}).catch((err) => {
})
十一、完整代码
第一步到第八步代码
import axios from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus'//生产环境下读取 public=>static=>config.js 中的 api 变量(用于上线后动态配置接口地址)
const BASE_URL =
process.env.NODE_ENV === 'development'
? process.env.VUE_APP_API_URL
: window.webConfig.webApiBaseUrlconst request = axios.create({
baseURL: BASE_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})request.interceptors.request.use((config) => {
config.headers = config.headers || {}, //设置请求头
config.headers['Token'] = localStorage.getItem('Token') || ''
config.headers['Code'] = localStorage.getItem('Code') || ''
return config
})//是否刷新 token
let isRefreshing = false
//请求队列
let requestList = [] as any[]request.interceptors.response.use(
async (res: any) => {
// 路由
const Code: number = res.data.Code || 0const config = res.config//处理token过期==>更新tokenif (Code == -1) {if (!isRefreshing) {isRefreshing = true//id (用于获取token)const appid = process.env.VUE_APP_APPID.replace(/"/g, '')//密钥 (用于获取token)const secret = process.env.VUE_APP_SECRET.replace(/"/g, '')//请求获取token接口return (request({url: `outside/getAccessToken?appid=${appid}&secret=${secret}`,method: 'GET',}).then((res) => {//存token缓存localStorage.setItem('Token', res.data.data)config.headers.token = res.data.datalet response = request(config)// 刷新成功后将请求队列里的请求依次执行requestList.forEach((cb) => cb())requestList = [] // 执行完毕后将请求队列置空return response}).finally(() => {isRefreshing = false}))} else {return new Promise((resolve) => {requestList.push(() => {config.headers.token = localStorage.getItem('Token')let response = request(config)resolve(response)})})}} else if (Code == 0) {//处理code过期==>退出登录 (tokenExpired是防止code过期后,下方提示消息显示多次)let tokenExpired = localStorage.getItem('tokenExpired')if (tokenExpired == 'false' || !tokenExpired) {localStorage.setItem('tokenExpired', 'true')ElMessageBox.confirm('登录已过期,请重新登录!', '提示', {confirmButtonText: '确定',showClose: false,showCancelButton: false,closeOnClickModal: false,type: 'warning',}).then(() => {//清除缓存localStorage.removeItem('Token')localStorage.removeItem('UserGlobalStore')localStorage.removeItem('Code')// 重新加载页面(清除路由缓存)window.location.reload()// 在页面加载完成后跳转到登录页面window.onload = function () {window.location.href = '/login'}})} else {//清除缓存localStorage.removeItem('Token')localStorage.removeItem('UserGlobalStore')localStorage.removeItem('Code')// 重新加载页面(清除路由缓存)window.location.reload()// 在页面加载完成后跳转到登录页面window.onload = function () {window.location.href = '/login'}}} else {return Promise.resolve(res)}},
(err) => {
//接口出错
ElMessage.error(err.message)
return Promise.reject(err)
}
)export default request