Files
timeline-mobile/src/services/apiClient.ts
jianghao 42d332f77c feat: 初始化移动端项目基础结构
添加项目基础配置和核心功能模块:
- 配置 TypeScript 和 React Native 环境
- 实现认证状态管理
- 封装 API 请求客户端
- 搭建应用导航框架
2026-02-24 10:34:59 +08:00

139 lines
3.4 KiB
TypeScript
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.
/**
* apiClient - API 请求客户端
*
* 功能描述:
* 封装 Axios 请求客户端,处理:
* - 请求拦截(添加 Token
* - 响应拦截(错误处理)
* - Token 刷新
* - 网络状态检测
*
* @author Timeline Team
* @date 2024
*/
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';
import { useAuthStore } from '@stores/authStore';
import NetInfo from '@react-native-community/netinfo';
// API 基础配置
const BASE_URL = __DEV__
? 'http://10.0.2.2:8080' // Android 模拟器
: 'https://api.timeline.com';
const TIMEOUT = 30000;
/**
* 创建 Axios 实例
*/
const apiClient: AxiosInstance = axios.create({
baseURL: BASE_URL,
timeout: TIMEOUT,
headers: {
'Content-Type': 'application/json',
},
});
/**
* 请求拦截器
* 添加 Token 到请求头
*/
apiClient.interceptors.request.use(
async (config: InternalAxiosRequestConfig) => {
// 检查网络状态
const netInfo = await NetInfo.fetch();
if (!netInfo.isConnected) {
return Promise.reject(new Error('网络不可用,请检查网络连接'));
}
// 添加 Token
const token = useAuthStore.getState().token;
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
/**
* 响应拦截器
* 处理错误和 Token 刷新
*/
apiClient.interceptors.response.use(
(response) => {
return response.data;
},
async (error: AxiosError) => {
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
// 处理 401 未授权错误
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// 尝试刷新 Token
const refreshToken = useAuthStore.getState().refreshToken;
if (refreshToken) {
const response = await axios.post(`${BASE_URL}/auth/refresh`, {
refreshToken,
});
const { token } = response.data;
useAuthStore.getState().setToken(token);
// 重试原请求
if (originalRequest.headers) {
originalRequest.headers.Authorization = `Bearer ${token}`;
}
return apiClient(originalRequest);
}
} catch (refreshError) {
// Token 刷新失败,登出
useAuthStore.getState().logout();
return Promise.reject(refreshError);
}
}
// 处理其他错误
const errorMessage = getErrorMessage(error);
return Promise.reject(new Error(errorMessage));
}
);
/**
* 获取错误消息
*/
function getErrorMessage(error: AxiosError): string {
if (error.message === 'Network Error') {
return '网络错误,请检查网络连接';
}
if (error.response) {
const status = error.response.status;
const data: any = error.response.data;
switch (status) {
case 400:
return data?.message || '请求参数错误';
case 401:
return '登录已过期,请重新登录';
case 403:
return '没有权限访问';
case 404:
return '请求的资源不存在';
case 500:
return '服务器错误,请稍后重试';
default:
return data?.message || `请求失败 (${status})`;
}
}
return error.message || '未知错误';
}
export default apiClient;