Files
sionrui/frontend/api/axios/client.js

157 lines
3.6 KiB
JavaScript
Raw Normal View History

2025-11-13 01:06:28 +08:00
/**
* Mono 级别的 C Axios 实例
* monorepo 中所有应用使用的统一 HTTP 客户端
*/
import axios from 'axios'
// 直接使用实例(最简单、最可靠)
import tokenManager from '@gold/utils/token-manager'
2025-11-13 01:06:28 +08:00
/**
* 不需要 token 的接口白名单
*/
const WHITE_LIST = [
'/auth/login',
'/auth/send-sms-code',
'/auth/sms-login',
'/auth/validate-sms-code',
'/auth/register',
'/auth/reset-password',
'/auth/refresh-token',
]
/**
* 检查 URL 是否在白名单中
*/
function isInWhiteList(url) {
if (!url) return false
return WHITE_LIST.some((path) => url.includes(path))
}
/**
* 处理 401 未授权错误
* 注意只做清理工作不处理重定向重定向由上层回调处理
2025-11-13 01:06:28 +08:00
*/
let isHandling401 = false
function handle401Error(error) {
2025-11-13 01:06:28 +08:00
if (isHandling401) return
2025-11-13 01:06:28 +08:00
isHandling401 = true
2025-11-13 01:06:28 +08:00
try {
// 清空token
tokenManager.clearTokens()
console.warn('Token已清空因401错误')
2025-11-13 01:06:28 +08:00
} catch (e) {
console.error('清空 token 失败:', e)
}
2025-11-13 01:06:28 +08:00
// 延迟重置标志
setTimeout(() => {
isHandling401 = false
}, 2000)
}
/**
* 创建 C Axios 实例
* @param {Object} options - 配置选项
* @param {string} options.baseURL - 基础 URL
* @param {number} options.timeout - 超时时间毫秒
* @param {Function} options.on401 - 401 错误处理函数
* @param {Function} options.on403 - 403 错误处理函数
* @returns {AxiosInstance} Axios 实例
*/
export function createClientAxios(options = {}) {
const {
baseURL = '/',
timeout = 180000,
on401 = handle401Error,
on403 = null,
} = options
const client = axios.create({
baseURL,
timeout,
})
// 请求拦截器
client.interceptors.request.use((config) => {
// 添加 tenant-id
const tenantId =
(typeof import.meta !== 'undefined' && import.meta.env?.VITE_TENANT_ID) ||
(typeof process !== 'undefined' && process.env?.VITE_TENANT_ID) ||
'1'
if (tenantId) {
config.headers['tenant-id'] = tenantId
}
// 添加 Authorization header
const needToken = config.headers?.isToken !== false && !isInWhiteList(config.url || '')
if (needToken) {
const authHeader = tokenManager.getAuthHeader()
2025-11-13 01:06:28 +08:00
if (authHeader) {
config.headers.Authorization = authHeader
}
}
return config
})
// 响应拦截器
client.interceptors.response.use(
(response) => {
const data = response.data
2025-11-13 01:06:28 +08:00
// 检查业务状态码
if (data && typeof data.code === 'number') {
if (data.code === 0 || data.code === 200) {
return data
}
// 创建业务错误对象
const error = new Error(data?.message || data?.msg || '请求失败')
error.code = data?.code
error.data = data
// 业务码 401/403 只在响应拦截器处理,避免重复处理
2025-11-13 01:06:28 +08:00
if (data.code === 401 && typeof on401 === 'function') {
on401(error)
2025-11-13 01:06:28 +08:00
}
2025-11-13 01:06:28 +08:00
if (data.code === 403 && typeof on403 === 'function') {
on403(error)
2025-11-13 01:06:28 +08:00
}
// 抛出错误,业务代码可以捕获
2025-11-13 01:06:28 +08:00
return Promise.reject(error)
}
2025-11-13 01:06:28 +08:00
return data
},
(error) => {
// HTTP 状态码 401/403 只在错误拦截器处理
2025-11-13 01:06:28 +08:00
if (error.response?.status === 401 && typeof on401 === 'function') {
on401(error)
2025-11-13 01:06:28 +08:00
}
2025-11-13 01:06:28 +08:00
if (error.response?.status === 403 && typeof on403 === 'function') {
on403(error)
2025-11-13 01:06:28 +08:00
}
2025-11-13 01:06:28 +08:00
return Promise.reject(error)
}
)
return client
}
/**
* 默认导出的 C Axios 实例
* 可在应用层覆盖配置
*/
export const clientAxios = createClientAxios()
export default clientAxios