Files
sionrui/frontend/api/axios/client.js
sion123 fb6d18b4f5 feat: 重构HTTP客户端架构和认证系统
核心改进:
- HTTP客户端:工厂函数模式,支持自定义拦截器和401/403处理
- 认证服务:函数式实现,消除this绑定问题,支持业务码+HTTP状态码双通道
- Token管理:简化为直接实例导出,移除bind()和箭头函数包装
- 路由守卫:优化逻辑,移除冗余代码,更简洁易维护

技术亮点:
- 统一401/403错误处理(业务code和HTTP status双检查)
- 自动刷新token并重试请求,保留自定义拦截器
- 分层清晰:clientAxios (Mono) -> http (应用) -> AuthService
- 支持扩展:业务代码可创建自定义HTTP实例并添加拦截器

文件变更:
- 新增 AuthService.js (函数式) 和 Login.vue
- 重构 http.js、token-manager.js、router/index.js
- 删除 TokenInput.vue、utils/auth.js 等冗余文件
- 更新所有API调用点使用直接实例导入

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 00:58:51 +08:00

154 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Mono 级别的 C 端 Axios 实例
* 供 monorepo 中所有应用使用的统一 HTTP 客户端
*/
import axios from 'axios'
// 直接使用实例(最简单、最可靠)
import tokenManager from '@gold/utils/token-manager'
/**
* 不需要 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 未授权错误
*/
let isHandling401 = false
function handle401Error() {
if (isHandling401) return
isHandling401 = true
try {
tokenManager.clearTokens()
} catch (e) {
console.error('清空 token 失败:', e)
}
// 延迟重置标志
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()
if (authHeader) {
config.headers.Authorization = authHeader
}
}
return config
})
// 响应拦截器
client.interceptors.response.use(
(response) => {
const data = response.data
// 检查业务状态码
if (data && typeof data.code === 'number') {
if (data.code === 0 || data.code === 200) {
return data
}
// 处理 401
if (data.code === 401 && typeof on401 === 'function') {
on401()
}
// 处理 403业务状态码
if (data.code === 403 && typeof on403 === 'function') {
on403()
}
// 抛出业务错误
const error = new Error(data?.message || data?.msg || '请求失败')
error.code = data?.code
error.data = data
return Promise.reject(error)
}
return data
},
(error) => {
// 处理 HTTP 401
if (error.response?.status === 401 && typeof on401 === 'function') {
on401()
}
// 处理 HTTP 403
if (error.response?.status === 403 && typeof on403 === 'function') {
on403()
}
return Promise.reject(error)
}
)
return client
}
/**
* 默认导出的 C 端 Axios 实例
* 可在应用层覆盖配置
*/
export const clientAxios = createClientAxios()
export default clientAxios