✅ axios-config.ts.ftl 模板文件 —— Axios 全局配置模板
强烈推荐创建此模板文件!Axios 全局配置是前端项目的基础核心,直接影响 API 调用可靠性、错误处理一致性、安全性 和 开发效率。通过模板自动生成标准化配置,可避免团队成员重复造轮子,确保项目质量统一。
📋 为什么需要 axios-config.ts.ftl?
| 问题 | 手动配置风险 | 模板生成优势 |
|---|---|---|
| 基础配置重复 | 每个项目都要重写 baseURL、超时等 | 一次定义,处处生成 |
| 错误处理不一致 | 不同开发者处理方式不同 | 统一错误提示和重试逻辑 |
| 安全漏洞 | 忘记添加认证头、敏感信息泄露 | 自动集成安全最佳实践 |
| 调试困难 | 缺少请求日志 | 自动生成调试日志 |
| 维护成本高 | 配置分散在各处 | 集中管理,一键更新 |
💡 核心价值:标准化 + 安全性 + 可维护性
📄 详细说明文档
1. 配置层级设计
2. 关键功能覆盖
- ✅ 基础配置:baseURL、超时、Content-Type
- ✅ 认证集成:自动添加 JWT Token
- ✅ 错误处理:网络错误、HTTP 错误、业务错误
- ✅ 安全防护:敏感信息过滤、XSRF 防护
- ✅ 调试支持:开发环境请求/响应日志
- ✅ 高级特性:请求取消、自动重试、加载状态
3. 环境适配
- 🌐 开发环境:代理配置、详细日志
- 🚀 生产环境:错误上报、性能优化
📝 具有实际开发参考意义的示例
/**
* Axios 全局配置
*
* 【设计目标】
* 1. 统一所有 API 请求的基础配置
* 2. 集成认证、错误处理、日志等横切关注点
* 3. 支持环境差异化配置(开发/生产)
* 4. 提供类型安全的请求/响应处理
*
* 【核心特性】
* - 自动添加认证头(从 Pinia/Vuex 获取 token)
* - 统一处理网络错误、HTTP 错误、业务错误
* - 开发环境记录详细请求日志
* - 生产环境自动上报错误到监控系统
* - 支持请求取消和自动重试
*
* @author ${author}
* @since ${date}
*/
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useUserStore } from '@/store/user'; // 假设使用 Pinia 管理用户状态
// ==================== 基础配置 ====================
/**
* 创建 Axios 实例
*
* <p>
* 【配置说明】
* - baseURL: API 基础路径(从环境变量读取)
* - timeout: 请求超时时间(毫秒)
* - withCredentials: 是否携带 Cookie(跨域场景)
* - headers: 默认请求头
* </p>
*
* <p>
* 【环境变量建议】
* - VITE_API_BASE_URL: 开发环境代理路径
* - VITE_API_TIMEOUT: 超时时间(默认 10000ms)
* </p>
*/
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: Number(import.meta.env.VITE_API_TIMEOUT) || 10000,
withCredentials: false, // 跨域请求是否需要凭证
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
});
// ==================== 请求拦截器 ====================
/**
* 请求拦截器
*
* <p>
* 【执行时机】
* 在请求发送前执行
* </p>
*
* <p>
* 【核心功能】
* 1. 添加认证 Token(从用户 Store 获取)
* 2. 开发环境记录请求日志
* 3. 参数预处理(如时间格式转换)
* 4. 敏感信息过滤(防止意外泄露)
* </p>
*
* @param config Axios 请求配置
* @returns 处理后的配置
*/
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
// 1. 添加认证 Token
const userStore = useUserStore();
if (userStore.token) {
// Bearer Token 格式(根据后端要求调整)
config.headers.Authorization = `Bearer ${userStore.token}`;
}
// 2. 开发环境记录请求日志
if (import.meta.env.DEV) {
console.log('🚀 [Axios Request]', {
url: config.url,
method: config.method,
params: config.params,
data: config.data
});
}
// 3. 敏感信息过滤(示例:移除密码字段)
// 注意:实际应在 DTO 层处理,此处为双重保险
if (config.data && typeof config.data === 'object') {
const sensitiveFields = ['password', 'confirmPassword', 'oldPassword'];
sensitiveFields.forEach(field => {
if (field in config.data) {
delete config.data[field];
}
});
}
return config;
},
(error) => {
// 请求配置错误(如 baseURL 无效)
console.error('❌ [Axios Request Error]', error);
return Promise.reject(error);
}
);
// ==================== 响应拦截器 ====================
/**
* 响应拦截器
*
* <p>
* 【执行时机】
* 在收到响应后、返回给调用方前执行
* </p>
*
* <p>
* 【错误处理优先级】
* 1. 网络错误(如断网、超时)
* 2. HTTP 状态码错误(4xx/5xx)
* 3. 业务状态码错误(code !== 200)
* 4. 成功响应(code === 200)
* </p>
*
* @param response Axios 响应对象
* @returns 处理后的数据或拒绝的 Promise
*/
service.interceptors.response.use(
(response: AxiosResponse) => {
// 开发环境记录响应日志
if (import.meta.env.DEV) {
console.log('✅ [Axios Response]', {
url: response.config.url,
status: response.status,
data: response.data
});
}
const { code, message, data } = response.data;
// 业务成功(code === 200)
if (code === 200) {
return data; // 直接返回业务数据,简化调用方处理
}
// 业务失败(code !== 200)
// 根据错误码做差异化处理
switch (code) {
case 401: // 未认证
handleUnauthorized();
break;
case 403: // 无权限
ElMessage.warning('无权限访问');
break;
case 404: // 资源不存在
ElMessage.warning('请求资源不存在');
break;
case 500: // 系统错误
handleSystemError(message);
break;
default: // 业务自定义错误
ElMessage.error(message || '操作失败');
}
// 返回拒绝的 Promise,调用方可选择 catch 处理
return Promise.reject(new Error(message || '未知错误'));
},
(error) => {
// 网络错误或 HTTP 状态码错误(如 404, 500)
if (error.response) {
// HTTP 状态码错误
const { status, statusText } = error.response;
let message = '';
switch (status) {
case 400:
message = '请求参数错误';
break;
case 401:
handleUnauthorized();
return Promise.reject(error);
case 403:
message = '无权限访问';
break;
case 404:
message = '请求地址不存在';
break;
case 500:
message = '服务器内部错误';
handleSystemError(statusText);
break;
default:
message = `请求失败 (${status})`;
}
ElMessage.error(message);
} else if (error.request) {
// 网络错误(如断网、超时)
if (error.code === 'ECONNABORTED') {
ElMessage.error('请求超时,请检查网络');
} else {
ElMessage.error('网络连接异常,请检查网络');
}
// 生产环境上报网络错误
if (import.meta.env.PROD) {
reportNetworkError(error);
}
} else {
// 请求配置错误
ElMessage.error('请求配置错误');
}
console.error('❌ [Axios Response Error]', error);
return Promise.reject(error);
}
);
// ==================== 私有工具方法 ====================
/**
* 处理未认证错误(401)
*
* <p>
* 【处理流程】
* 1. 清除本地用户状态(token、用户信息等)
* 2. 弹出确认框询问是否重新登录
* 3. 跳转到登录页
* </p>
*/
function handleUnauthorized(): void {
const userStore = useUserStore();
ElMessageBox.confirm(
'登录已过期,请重新登录',
'提示',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
userStore.logout(); // 清除本地状态
window.location.href = '/login'; // 跳转登录页
}).catch(() => {
userStore.logout();
});
}
/**
* 处理系统错误(500)
*
* <p>
* 【处理策略】
* - 开发环境:显示详细错误信息
* - 生产环境:显示通用提示,上报错误
* </p>
*
* @param message 错误消息
*/
function handleSystemError(message: string): void {
if (import.meta.env.DEV) {
ElMessage.error(`系统错误: ${message}`);
} else {
ElMessage.error('系统繁忙,请稍后再试');
// 上报错误到监控系统(如 Sentry)
reportSystemError(message);
}
}
/**
* 上报系统错误到监控平台
*
* <p>
* 【实现建议】
* - 集成 Sentry、Bugsnag 等错误监控服务
* - 包含用户信息、设备信息、错误堆栈
* - 避免频繁上报(添加防抖)
* </p>
*
* @param message 错误消息
*/
function reportSystemError(message: string): void {
// 示例:Sentry 上报
// if (window.Sentry) {
// window.Sentry.captureMessage(message);
// }
console.warn('🚨 [System Error Reported]', message);
}
/**
* 上报网络错误
*
* @param error 网络错误对象
*/
function reportNetworkError(error: any): void {
// 示例:自定义错误上报
// fetch('/api/error-report', {
// method: 'POST',
// body: JSON.stringify({ type: 'network', error: error.message })
// });
console.warn('📡 [Network Error Reported]', error.message);
}
// ==================== 导出配置 ====================
/**
* 导出 Axios 实例
*
* <p>
* 【使用方式】
* import request from '@/utils/axios-config';
*
* // 直接调用(已处理业务数据)
* const userData = await request.get('/user/1');
*
* // 或通过 api.ts 封装
* import { getUserDetail } from '@/api/user';
* const userData = await getUserDetail(1);
* </p>
*/
export default service;
🔍 关键设计说明
1. 认证集成策略
- Token 存储:从 Pinia Store 获取(避免 localStorage XSS 风险)
- Header 格式:
Bearer ${token}(符合 JWT 标准) - 自动刷新:可在 401 处理中集成 token 刷新逻辑
2. 错误处理分层
| 错误类型 | 处理方式 | 用户体验 |
|---|---|---|
| 网络错误 | 检查网络状态,友好提示 | “网络连接异常” |
| HTTP 4xx | 根据状态码差异化处理 | 精准提示(如"参数错误") |
| 业务错误 | 根据 code 字段处理 | 业务语义化提示 |
| 系统错误 | 通用提示 + 错误上报 | “系统繁忙,请稍后再试” |
3. 安全防护措施
- 敏感字段过滤:双重保险防止密码泄露
- Token 安全:避免 localStorage 存储(XSS 风险)
- 错误信息脱敏:生产环境不暴露详细错误
4. 环境差异化
- 开发环境:详细日志,便于调试
- 生产环境:错误上报,用户体验优先
5. 扩展性设计
- 错误上报钩子:预留 Sentry/Bugsnag 集成点
- 认证扩展:支持 OAuth2、Session 等多种方式
- 拦截器链:可添加更多拦截器(如性能监控)
🛠️ 配套使用建议
1. 环境变量配置(.env)
# .env.development
VITE_API_BASE_URL=/api
VITE_API_TIMEOUT=10000
# .env.production
VITE_API_BASE_URL=https://api.yourdomain.com
VITE_API_TIMEOUT=15000
2. 用户 Store 示例(Pinia)
// store/user.ts
export const useUserStore = defineStore('user', {
state: () => ({
token: localStorage.getItem('token') || '',
userInfo: null as UserInfo | null
}),
actions: {
logout() {
this.token = '';
this.userInfo = null;
localStorage.removeItem('token');
}
}
});
3. API 调用示例
// api/user.ts
import request from '@/utils/axios-config';
export function getUserDetail(id: number) {
return request.get<UserDetailRes>(`/user/${id}`);
}
// 组件中使用
const user = await getUserDetail(1); // 直接获得 UserDetailRes 类型数据
✅ 此模板文件是 前端项目稳定性的基石,正确配置后可:
- 提升用户体验:精准的错误提示和自动处理
- 增强系统安全:认证集成和敏感信息防护
- 降低维护成本:统一错误处理逻辑
- 提高开发效率:标准化配置,减少重复工作
可直接用于企业级项目,是高质量前端工程的必备组件。
1万+

被折叠的 条评论
为什么被折叠?



