vue3基于axios配置请求拦截器响应拦截器以及无感刷新token的实现
一、下载并引入 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 || 0
const config = res.config
//处理token过期==>更新token
if (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.data
let 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.webApiBaseUrl
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
let isRefreshing = false
//请求队列
let requestList = [] as any[]
request.interceptors.response.use(
async (res: any) => {
// 路由
const Code: number = res.data.Code || 0
const config = res.config
//处理token过期==>更新token
if (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.data
let 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