Axios 教程 - 基于 Promise 的 HTTP 客户端
项目概述
Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境,提供了简洁、强大的 API 来处理 HTTP 请求。
- 项目链接:https://github.com/axios/axios
- 官方网站:https://axios-http.com/
- GitHub Stars:100k+
核心功能
- HTTP 请求:支持 GET、POST、PUT、DELETE 等 HTTP 方法
- 拦截器:支持请求和响应拦截器
- 转换请求和响应:自动转换 JSON 数据,支持自定义转换
- 自动转换 JSON 数据:自动将请求和响应数据转换为 JSON
- 取消请求:支持取消正在进行的请求
- 超时处理:支持设置请求超时
- 错误处理:统一的错误处理机制
- 并发请求:支持同时发送多个请求
- 浏览器支持:支持所有现代浏览器
- Node.js 支持:支持 Node.js 环境
- TypeScript 支持:良好的 TypeScript 类型定义
安装与设置
基本安装
# 安装 Axios
npm install axios
# 安装特定版本
npm install axios@1.6.0基本设置
// 导入 Axios
import axios from 'axios';
// 或者使用 CommonJS 导入
const axios = require('axios');
// 创建一个实例
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
headers: {
'Content-Type': 'application/json'
}
});基本使用
发送 GET 请求
// 基本 GET 请求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
// 使用参数对象
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
// 使用 async/await
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response.data);
} catch (error) {
console.log(error);
}
}发送 POST 请求
// 基本 POST 请求
axios.post('/user', {
firstName: 'John',
lastName: 'Doe'
})
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
// 使用 async/await
async function createUser() {
try {
const response = await axios.post('/user', {
firstName: 'John',
lastName: 'Doe'
});
console.log(response.data);
} catch (error) {
console.log(error);
}
}其他 HTTP 方法
// PUT 请求
axios.put('/user/12345', {
firstName: 'Jane',
lastName: 'Smith'
});
// DELETE 请求
axios.delete('/user/12345');
// PATCH 请求
axios.patch('/user/12345', {
firstName: 'Jane'
});
// HEAD 请求
axios.head('/user/12345');
// OPTIONS 请求
axios.options('/user/12345');响应结构
axios.get('/user/12345')
.then(function (response) {
// 响应数据
console.log(response.data);
// 状态码
console.log(response.status);
// 状态文本
console.log(response.statusText);
// 响应头
console.log(response.headers);
// 配置
console.log(response.config);
});高级特性
拦截器
// 添加请求拦截器
axios.interceptors.request.use(
function (config) {
// 在发送请求之前做些什么
// 例如添加认证令牌
config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
return config;
},
function (error) {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 添加响应拦截器
axios.interceptors.response.use(
function (response) {
// 对响应数据做点什么
return response;
},
function (error) {
// 对响应错误做点什么
if (error.response) {
// 服务器返回错误状态码
switch (error.response.status) {
case 401:
// 未授权,重定向到登录页
window.location.href = '/login';
break;
case 403:
// 禁止访问
console.log('Access forbidden');
break;
case 404:
// 资源不存在
console.log('Resource not found');
break;
case 500:
// 服务器错误
console.log('Server error');
break;
default:
console.log('An error occurred');
}
} else if (error.request) {
// 请求已发送但没有收到响应
console.log('No response received');
} else {
// 请求配置出错
console.log('Error:', error.message);
}
return Promise.reject(error);
}
);
// 移除拦截器
const myInterceptor = axios.interceptors.request.use(function () { /*...*/ });
axios.interceptors.request.eject(myInterceptor);实例和配置
// 创建实例
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
}
});
// 实例方法
instance.get('/user')
.then(function (response) {
console.log(response);
});
// 全局配置
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = 'Bearer token';
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';取消请求
// 使用 CancelToken.source()
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user', {
cancelToken: source.token
})
.catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
// 取消请求
source.cancel('Operation canceled by the user.');
// 使用执行器函数
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user', {
cancelToken: new CancelToken(function executor(c) {
// 执行器函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// 取消请求
cancel();并发请求
// 使用 Promise.all
function getUserData() {
return axios.get('/user/12345');
}
function getPosts() {
return axios.get('/user/12345/posts');
}
Promise.all([getUserData(), getPosts()])
.then(function (responses) {
const userData = responses[0].data;
const posts = responses[1].data;
console.log(userData, posts);
});
// 使用 axios.all
axios.all([
axios.get('/user/12345'),
axios.get('/user/12345/posts')
])
.then(axios.spread(function (userResponse, postsResponse) {
const userData = userResponse.data;
const posts = postsResponse.data;
console.log(userData, posts);
}));错误处理
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// 服务器返回错误状态码
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// 请求已发送但没有收到响应
console.log(error.request);
} else {
// 请求配置出错
console.log('Error:', error.message);
}
console.log(error.config);
});
// 自定义错误处理
axios.get('/user/12345')
.then(function (response) {
if (response.data.error) {
// 处理业务逻辑错误
return Promise.reject(new Error(response.data.error.message));
}
return response;
})
.catch(function (error) {
console.log('Error:', error.message);
});转换请求和响应
// 全局转换
axios.defaults.transformRequest = [function (data, headers) {
// 对请求数据进行转换
if (headers['Content-Type'] === 'application/x-www-form-urlencoded') {
return Object.entries(data)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
}
return data;
}];
axios.defaults.transformResponse = [function (data) {
// 对响应数据进行转换
return data;
}];
// 实例级转换
const instance = axios.create({
transformRequest: [function (data, headers) {
// 对请求数据进行转换
return data;
}],
transformResponse: [function (data) {
// 对响应数据进行转换
return data;
}]
});
// 请求级转换
axios.get('/user/12345', {
transformResponse: [function (data) {
// 对响应数据进行转换
return data;
}]
});实用场景
基本 API 调用
// 封装 API 调用
const api = {
getUsers() {
return axios.get('/users');
},
getUserById(id) {
return axios.get(`/users/${id}`);
},
createUser(user) {
return axios.post('/users', user);
},
updateUser(id, user) {
return axios.put(`/users/${id}`, user);
},
deleteUser(id) {
return axios.delete(`/users/${id}`);
}
};
// 使用
api.getUsers()
.then(response => {
console.log(response.data);
});文件上传
// 上传单个文件
const formData = new FormData();
formData.append('file', fileInput.files[0]);
axios.post('/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 上传多个文件
const formData = new FormData();
for (let i = 0; i < fileInput.files.length; i++) {
formData.append('files', fileInput.files[i]);
}
axios.post('/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
// 上传进度
axios.post('/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: function (progressEvent) {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(percentCompleted);
}
});文件下载
// 下载文件
axios.get('/download', {
responseType: 'blob'
})
.then(function (response) {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf');
document.body.appendChild(link);
link.click();
});
// 下载大文件
axios.get('/download', {
responseType: 'stream'
})
.then(function (response) {
response.data.pipe(fs.createWriteStream('file.pdf'));
});处理跨域请求
// 配置跨域请求
const instance = axios.create({
baseURL: 'https://api.example.com',
headers: {
'Content-Type': 'application/json'
},
withCredentials: true // 允许携带凭证
});
// 发送跨域请求
instance.get('/user')
.then(function (response) {
console.log(response.data);
});最佳实践
- 创建实例:为不同的 API 端点创建不同的实例
- 使用拦截器:使用拦截器处理认证、日志记录等横切关注点
- 封装 API 调用:将 API 调用封装成函数或类,提高代码可维护性
- 错误处理:统一处理错误,提供友好的错误提示
- 取消请求:在组件卸载时取消未完成的请求,避免内存泄漏
- 使用 async/await:使用 async/await 使代码更简洁易读
- 设置合理的超时:为请求设置合理的超时时间
- 使用 TypeScript:使用 TypeScript 提供类型安全
- 测试:为 API 调用编写测试
- 文档:为 API 调用编写文档
常见问题与解决方案
1. CORS 错误
问题:跨域请求被浏览器阻止
解决方案:
- 在服务器端设置 CORS 头
- 使用代理服务器
- 在 Axios 实例中设置
withCredentials: true
2. 认证令牌管理
问题:如何管理认证令牌
解决方案:
- 使用请求拦截器自动添加认证令牌
- 在响应拦截器中处理令牌过期
- 将令牌存储在 localStorage 或 sessionStorage 中
3. 请求超时
问题:请求超时
解决方案:
- 设置合理的超时时间
- 实现重试机制
- 显示加载状态,提高用户体验
4. 取消请求
问题:如何取消未完成的请求
解决方案:
- 使用 CancelToken 取消请求
- 在组件卸载时取消请求
- 在用户操作导致不需要的请求时取消
5. 大数据传输
问题:传输大文件或大量数据时性能问题
解决方案:
- 使用流式传输
- 实现分块上传/下载
- 显示传输进度
6. 并发请求限制
问题:同时发送过多请求导致性能问题
解决方案:
- 实现请求队列
- 限制并发请求数量
- 使用缓存避免重复请求
与其他 HTTP 客户端的比较
Axios vs Fetch API
- API 简洁性:Axios API 更简洁,使用 Promise
- 浏览器兼容性:Axios 支持旧浏览器,Fetch API 只支持现代浏览器
- 功能丰富度:Axios 提供更多功能,如拦截器、取消请求等
- 错误处理:Axios 自动将 4xx/5xx 响应视为错误,Fetch API 只在网络错误时视为错误
- 配置:Axios 支持全局配置,Fetch API 每次请求都需要配置
Axios vs jQuery.ajax
- 依赖:Axios 无依赖,jQuery.ajax 依赖 jQuery
- 体积:Axios 体积更小
- 功能:两者功能相似,但 Axios API 更现代
- 生态系统:Axios 生态系统更活跃
- TypeScript 支持:Axios 有良好的 TypeScript 支持
Axios vs SuperAgent
- API 风格:两者 API 风格相似
- 功能:两者功能相似
- 生态系统:Axios 生态系统更活跃
- 社区支持:Axios 社区支持更好
- TypeScript 支持:Axios 有更好的 TypeScript 支持
参考资源
- 官方文档:https://axios-http.com/docs/intro
- GitHub 仓库:https://github.com/axios/axios
- Axios 示例:https://axios-http.com/docs/example
- 拦截器文档:https://axios-http.com/docs/interceptors
- 错误处理文档:https://axios-http.com/docs/handling_errors
- 取消请求文档:https://axios-http.com/docs/cancellation
- TypeScript 支持:https://axios-http.com/docs/typeScript_intro