feat: 优化

This commit is contained in:
2026-01-18 02:55:02 +08:00
parent 85073b7960
commit a0378b5cbd
3 changed files with 165 additions and 200 deletions

View File

@@ -24,8 +24,7 @@ const WHITE_LIST = [
* 检查 URL 是否在白名单中
*/
function isInWhiteList(url) {
if (!url) return false
return WHITE_LIST.some((path) => url.includes(path))
return !!(url && WHITE_LIST.some(path => url.includes(path)))
}
/**
@@ -59,23 +58,34 @@ function onRefreshed() {
* 注意:只做清理工作,不处理重定向(重定向由上层回调处理)
*/
let isHandling401 = false
let handling401Timeout = null
function handle401Error(error) {
if (isHandling401) return
isHandling401 = true
try {
// 清空token
tokenManager.clearTokens()
console.warn('Token已清空因401错误')
} catch (e) {
console.error('清空 token 失败:', e)
// 防止重复处理
if (isHandling401 || error?._handled) {
return
}
// 延迟重置标志
setTimeout(() => {
isHandling401 = true
error && (error._handled = true)
try {
tokenManager.clearTokens()
const message = error?.needRelogin
? '[HTTP] Token已清空refreshToken无效需要重新登录'
: '[HTTP] Token已清空因401错误'
console.warn(message)
} catch (e) {
console.error('[HTTP] 清空 token 失败:', e)
}
if (handling401Timeout) {
clearTimeout(handling401Timeout)
}
handling401Timeout = setTimeout(() => {
isHandling401 = false
}, 2000)
}, 3000)
}
/**
@@ -117,66 +127,71 @@ export function createClientAxios(options = {}) {
// 检查是否需要认证
const needToken = config.headers?.isToken !== false && !isInWhiteList(config.url || '')
if (needToken) {
// 检查 token 是否即将过期30秒缓冲
const BUFFER_TIME = 30 * 1000
const currentToken = tokenManager.getAccessToken()
if (!needToken) {
return config
}
if (!currentToken) {
console.warn('[Token] 没有可用的 accessToken')
// 检查 token 是否即将过期30秒缓冲
const BUFFER_TIME = 30 * 1000
const currentToken = tokenManager.getAccessToken()
if (!currentToken) {
console.warn('[Token] 没有可用的 accessToken')
return config
}
const isTokenExpired = tokenManager.isExpired(BUFFER_TIME)
if (!isTokenExpired) {
// Token 未过期,直接添加 Authorization 头
const authHeader = tokenManager.getAuthHeader()
if (authHeader) {
config.headers.Authorization = authHeader
}
return config
}
// Token 即将过期,需要刷新
console.info('[Token] Token刷新')
// 如果不在刷新过程中,启动刷新
if (!isRefreshing) {
isRefreshing = true
if (!refreshTokenFn || typeof refreshTokenFn !== 'function') {
console.warn('[Token] 未提供刷新函数,跳过刷新')
isRefreshing = false
onRefreshed()
return config
}
const isTokenExpired = tokenManager.isExpired(BUFFER_TIME)
if (isTokenExpired) {
console.info('[Token] Token刷新')
// 如果不在刷新过程中,启动刷新
if (!isRefreshing) {
isRefreshing = true
// 执行刷新(使用上层传入的刷新函数)
if (refreshTokenFn && typeof refreshTokenFn === 'function') {
refreshTokenFn()
.then(() => {
console.info('[Token] 刷新成功')
isRefreshing = false
onRefreshed()
})
.catch((error) => {
isRefreshing = false
onRefreshed()
console.error('[Token] 刷新失败:', error.message)
})
} else {
console.warn('[Token] 未提供刷新函数,跳过刷新')
isRefreshing = false
onRefreshed()
}
}
// 等待刷新完成
return new Promise((resolve) => {
subscribeTokenRefresh(() => {
// 刷新完成后,重新获取 token 并添加到请求头
const authHeader = tokenManager.getAuthHeader()
if (authHeader) {
config.headers.Authorization = authHeader
}
resolve(config)
})
refreshTokenFn()
.then(() => {
console.info('[Token] 刷新成功')
isRefreshing = false
onRefreshed()
})
} else {
// Token 未过期,直接添加 Authorization 头
.catch((error) => {
isRefreshing = false
onRefreshed()
console.error('[Token] 刷新失败:', error.message)
if (error.code === 401 && error.needRelogin) {
console.info('[Token] refreshToken无效需要重新登录')
}
})
}
// 等待刷新完成
return new Promise((resolve) => {
subscribeTokenRefresh(() => {
const authHeader = tokenManager.getAuthHeader()
if (authHeader) {
config.headers.Authorization = authHeader
}
}
}
return config
resolve(config)
})
})
})
// 响应拦截器
@@ -195,31 +210,24 @@ export function createClientAxios(options = {}) {
error.code = data?.code
error.data = data
// 业务码 401/403 只在响应拦截器处理,避免重复处理
if (data.code === 401 && typeof on401 === 'function') {
on401(error)
}
// 业务码 403 只在响应拦截器处理,避免重复处理
if (data.code === 403 && typeof on403 === 'function') {
// 使用通用的权限不足错误,不使用后端返回的 message
const forbiddenError = new Error('权限不足,无法访问该资源')
forbiddenError.code = 403
on403(forbiddenError)
}
// 抛出错误,业务代码可以捕获
return Promise.reject(error)
}
return data
},
(error) => {
// HTTP 状态码 401/403 只在错误拦截器处理
if (error.response?.status === 401 && typeof on401 === 'function') {
on401(error)
}
const status = error.response?.status
if (error.response?.status === 403 && typeof on403 === 'function') {
if (status === 401 && typeof on401 === 'function') {
on401(error)
} else if (status === 403 && typeof on403 === 'function') {
const forbiddenError = new Error('权限不足,无法访问该资源')
forbiddenError.code = 403
on403(forbiddenError)