axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js。它具有拦截请求和响应、转换请求和响应数据、取消请求、自动转换 JSON 数据等功能,并且在 TypeScript 中提供了很好的类型支持。
安装
npm install axios
# 或者
yarn add axios
基本用法
- 发送 GET 请求
import axios from 'axios';
// 定义响应数据的类型
interface User {
id: number;
name: string;
email: string;
}
async function fetchUsers() {
try {
// 发送 GET 请求
const response = await axios.get<User[]>('https://jsonplaceholder.typicode.com/users');
// response.data 会被自动类型推断为 User[]
const users = response.data;
console.log('First user:', users[0].name);
return users;
} catch (error) {
// 处理错误
if (axios.isAxiosError(error)) {
console.error('Axios error:', error.message);
// 获取错误响应数据
if (error.response) {
console.error('Status:', error.response.status);
console.error('Data:', error.response.data);
}
} else {
console.error('Unexpected error:', error);
}
throw error;
}
}
// 使用示例
fetchUsers().then(users => console.log('Users:', users));
- 发送 POST 请求
interface Post {
id?: number; // id 是可选的,因为创建时可能还没有
title: string;
body: string;
userId: number;
}
async function createPost(postData: Omit<Post, 'id'>) {
try {
// 发送 POST 请求
const response = await axios.post<Post>('https://jsonplaceholder.typicode.com/posts', postData);
// 返回创建的帖子
return response.data;
} catch (error) {
console.error('Error creating post:', error);
throw error;
}
}
// 使用示例
createPost({
title: 'New Post',
body: 'This is a new post',
userId: 1
}).then(newPost => console.log('Created post:', newPost));
创建 axios 实例
import axios, { AxiosInstance, AxiosError } from 'axios';
// 创建 axios 实例
const apiClient: AxiosInstance = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000, // 请求超时时间
headers: {
'Content-Type': 'application/json',
'X-App-Version': '1.0.0'
}
});
// 设置认证令牌(如果有)
apiClient.defaults.headers.common['Authorization'] = 'Bearer your_token_here';
// 定义错误处理函数
function handleApiError(error: unknown) {
if (axios.isAxiosError(error)) {
const axiosError: AxiosError = error;
if (axiosError.response) {
// 请求已发送,服务器返回状态码超出了 2xx 范围
console.error('Response error:', axiosError.response.data);
console.error('Status code:', axiosError.response.status);
} else if (axiosError.request) {
// 请求已发送,但没有收到响应
console.error('No response received:', axiosError.request);
} else {
// 在设置请求时触发错误
console.error('Error setting up request:', axiosError.message);
}
} else {
// 处理非 axios 错误
console.error('Unexpected error:', error);
}
}
// 使用实例发送请求
async function fetchData() {
try {
const response = await apiClient.get('/data');
return response.data;
} catch (error) {
handleApiError(error);
throw error;
}
}
请求拦截器和响应拦截器
// 请求拦截器:在请求发送前添加认证信息
apiClient.interceptors.request.use(
(config) => {
// 从本地存储获取 token
const token = localStorage.getItem('auth_token');
// 如果 token 存在,则添加到请求头
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
// 处理请求错误
return Promise.reject(error);
}
);
// 响应拦截器:统一处理响应数据
apiClient.interceptors.response.use(
(response) => {
// 可以在这里对响应数据进行预处理
return response.data; // 直接返回 data,简化后续使用
},
(error) => {
// 统一处理错误
if (axios.isAxiosError(error)) {
if (error.response?.status === 401) {
// 处理未授权情况,例如跳转到登录页
console.log('Unauthorized, redirecting to login...');
}
}
return Promise.reject(error);
}
);
并发请求
使用 axios.all 可以同时处理多个请求:
async function fetchMultipleData() {
try {
// 并发发送多个请求
const [usersResponse, postsResponse] = await axios.all([
apiClient.get('/users'),
apiClient.get('/posts')
]);
const users = usersResponse.data;
const posts = postsResponse.data;
return { users, posts };
} catch (error) {
handleApiError(error);
throw error;
}
}
如何封装?
1. 创建基础配置和实例
首先定义默认配置并创建 axios 实例,设置基础 URL、超时时间等。
// src/services/axios.ts
import axios, { AxiosInstance, AxiosRequestConfig} from 'axios';
// 基础配置
const baseConfig: AxiosRequestConfig = {
baseURL: import.meta.env.VITE_API_BASE_URL || 'https://api.example.com',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
};
// 创建 axios 实例
const apiClient: AxiosInstance = axios.create(baseConfig);
export default apiClient;
2. 定义响应数据结构和错误类型
为了统一处理后端返回的数据格式,定义通用的响应接口和错误类型。
// src/types/api.ts
export interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
export class ApiError extends Error {
constructor(
public code: number,
message: string,
public data?: any
) {
super(message);
this.name = 'ApiError';
}
}
3. 添加请求拦截器
在请求发送前添加认证信息(如 Token)、处理请求参数等。
// src/services/axios.ts
import { getToken } from './auth'; // 假设存在 auth 模块获取 token
// 请求拦截器
apiClient.interceptors.request.use(
(config) => {
// 获取 token 并添加到请求头
const token = getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// 处理请求参数(示例:添加时间戳防止缓存)
if (config.method === 'get') {
config.params = {
...config.params,
_t: Date.now(),
};
}
return config;
},
(error: AxiosError) => {
console.error('Request error:', error.message);
return Promise.reject(error);
}
);
4. 添加响应拦截器
统一处理响应数据、错误码和异常情况。
// src/services/axios.ts
import { ApiResponse, ApiError } from '../types/api';
// 响应拦截器
apiClient.interceptors.response.use(
(response: AxiosResponse<ApiResponse>) => {
const { code, message, data } = response.data;
// 根据业务状态码处理响应
if (code === 200) {
return data; // 直接返回 data,简化后续使用
} else {
// 业务错误(如 403、500 等)
throw new ApiError(code, message, data);
}
},
(error: AxiosError) => {
// 网络错误或请求超时
if (error.code === 'ECONNABORTED') {
throw new ApiError(500, '请求超时,请稍后重试');
}
// HTTP 状态码错误
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 401:
// 未登录或 token 过期,跳转到登录页
console.log('未授权,跳转到登录页');
break;
case 403:
throw new ApiError(403, '权限不足', data);
case 404:
throw new ApiError(404, '资源不存在', data);
case 500:
throw new ApiError(500, '服务器内部错误', data);
default:
throw new ApiError(status, `请求错误 ${status}`, data);
}
}
// 其他错误
throw new ApiError(500, '网络连接失败,请检查网络设置', error.message);
}
);
5. 封装通用请求方法
提供更简洁的请求 API,支持泛型以确保返回类型的准确性。
// src/services/request.ts
import apiClient from './axios';
import { ApiResponse } from '../types/api';
// 通用请求方法
export const request = {
get: <T = any>(url: string, params?: any) =>
apiClient.get<ApiResponse<T>>(url, { params }).then(res => res.data),
post: <T = any>(url: string, data?: any) =>
apiClient.post<ApiResponse<T>>(url, data).then(res => res.data),
put: <T = any>(url: string, data?: any) =>
apiClient.put<ApiResponse<T>>(url, data).then(res => res.data),
delete: <T = any>(url: string, params?: any) =>
apiClient.delete<ApiResponse<T>>(url, { params }).then(res => res.data),
};
6. 使用封装后的请求方法
在业务代码中使用封装好的请求方法,获得类型提示和错误处理。
// src/services/userService.ts
import { request } from './request';
import { User } from '../types/user';
// 获取用户列表
export const fetchUsers = () =>
request.get<User[]>('/users');
// 创建用户
export const createUser = (user: Omit<User, 'id'>) =>
request.post<User>('/users', user);
// 业务组件中使用
import { fetchUsers, createUser } from '../services/userService';
// 获取用户列表
fetchUsers()
.then(users => console.log('用户列表:', users))
.catch(error => console.error('获取用户失败:', error.message));
// 创建用户
createUser({ name: '张三', age: 25 })
.then(newUser => console.log('创建成功:', newUser))
.catch(error => console.error('创建失败:', error.message));
7. 环境配置
根据不同环境配置不同的 API 地址。
// .env.development
VITE_API_BASE_URL = 'https://dev-api.example.com'
// .env.production
VITE_API_BASE_URL = 'https://api.example.com'