Axios 是什么

简介

Axios 是一个基于 Promise 的 HTTP 客户端,可以在浏览器和 Node.js 环境中使用。简化了前端发送 HTTP 请求的流程,提供了拦截器、自动解析 JSON 等强大功能。

它就是一个专门用来发送 HTTP 请求的工具库,相比浏览器原生的 XMLHttpRequest 或者 Fetch API,它有更简洁的语法、更强大的功能(比如拦截器、请求取消、自动转换 JSON 数据等),因此成为了前后端分离项目中对接后端接口的首选工具。

它的核心特点:

  1. 支持 Promise API,便于异步操作的处理(async/await 语法)。
  2. 拦截请求和响应(比如请求前添加 token、响应后统一处理错误)。
  3. 自动转换请求和响应数据(比如自动将 JSON 字符串转为 JavaScript 对象)。
  4. 取消请求。
  5. 客户端支持防御 XSRF(跨站请求伪造)。

在前后端分离架构中,前端(如 Vue/React/Angular)通过 Axios 向后端(如 Java/SpringBoot、Node.js/Express、Python/Django)的接口发送 HTTP 请求,后端处理请求后返回数据,前端再根据返回的数据渲染页面,这就是核心的对接流程。

那么 Vue 中也是一样,前端通过 Axios 发送 GET/POST 等请求到后端接口,后端处理请求后返回数据,前端通过 then/async/await 接收数据并处理,通过 catch 处理错误。

如何使用Axios

安装它和安其它包一样:npm install axios

1
2
3
4
5
# npm 安装
npm install axios --save

# yarn 安装
yarn add axios

安装完成后,就可以在组件或 js 文件中引入并发送请求了。以下是最基础的 GET、POST 请求示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 引入 Axios
import axios from 'axios';

// 1. 发送 GET 请求
// 例如:获取后端的用户列表接口,地址为 /api/users
axios.get('/api/users', {
// GET 请求的参数,会自动拼接到 URL 后(?id=1&name=zhangsan)
params: {
id: 1,
name: 'zhangsan'
}
})
.then(response => {
// 请求成功:处理后端返回的数据
console.log('请求成功,返回数据:', response.data);
})
.catch(error => {
// 请求失败:处理错误(比如网络错误、后端返回 404/500 等)
console.error('请求失败:', error);
});

// 2. 发送 POST 请求(常用于提交数据,比如新增用户)
// 例如:新增用户接口,地址为 /api/users
axios.post('/api/users', {
// POST 请求的参数(请求体)
username: 'lisi',
age: 20
})
.then(response => {
console.log('新增成功:', response.data);
})
.catch(error => {
console.error('新增失败:', error);
});

上述仅为演示,实际上,没有人在项目中真这么写,因此,我们会对 Axios 进行二次封装,抽离出公共的配置和逻辑。

我们会创建一个 Axios 实例并配置(如 src/utils/request.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import axios from 'axios';

// 1. 创建 Axios 实例(可以配置多个实例,对应不同的后端服务)
const service = axios.create({
// 基础路径:所有请求的 URL 都会拼接这个前缀(比如后端接口地址是 http://localhost:8080/api,这里配 /api,前端请求时只需要写 /users)
baseURL: '/api',
// 请求超时时间:超过 5 秒则请求失败
timeout: 5000
});

// 2. 请求拦截器:发送请求前的处理(比如添加 token、设置请求头)
service.interceptors.request.use(
config => {
// 示例:从本地存储中获取 token,添加到请求头(后端用于身份验证)
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
// 配置请求头为 JSON 格式(Axios 默认就是,可省略)
config.headers['Content-Type'] = 'application/json';
return config;
},
error => {
// 请求拦截器出错时的处理
console.error('请求拦截器错误:', error);
return Promise.reject(error);
}
);

// 3. 响应拦截器:接收响应后的处理(统一处理错误、统一解析数据)
service.interceptors.response.use(
response => {
// 响应成功:只返回后端的核心数据(比如后端返回 { code: 200, data: {}, msg: '成功' },我们只取 data)
const res = response.data;
// 示例:如果后端返回的 code 不是 200,说明业务逻辑失败(比如参数错误),抛出自定义错误
if (res.code !== 200) {
// 可以在这里统一提示错误(比如用 Element UI 的 Message 组件)
console.error('业务错误:', res.msg);
return Promise.reject(new Error(res.msg || '请求失败'));
} else {
return res; // 只返回 data,组件中直接使用即可
}
},
error => {
// 响应失败:统一处理 HTTP 错误(比如 401 未登录、404 接口不存在、500 服务器错误)
console.error('响应错误:', error);
// 示例:401 未登录,跳转到登录页
if (error.response && error.response.status === 401) {
// 这里可以写跳转到登录页的逻辑,比如:
// window.location.href = '/login';
console.error('未登录,请登录');
}
return Promise.reject(error);
}
);

// 导出封装后的 Axios 实例
export default service;

然后在需要的时候使用它,一般用在封装接口请求的时候(如 src/api/user.js),把所有和用户相关的接口抽离到一个文件中,便于维护:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 引入封装后的 Axios 实例
import request from '@/utils/request';

// 1. 获取用户列表
export function getUserList(params) {
// 这里的 URL 是 /users,会自动拼接 baseURL 变成 /api/users
return request({
url: '/users',
method: 'get',
params // 传入的参数(GET 请求)
});
}

// 2. 新增用户
export function addUser(data) {
return request({
url: '/users',
method: 'post',
data // 传入的参数(POST 请求体)
});
}

然后才会在 Vue组件中使用封装后的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { getUserList, addUser } from '@/api/user';

// 在组件的方法中调用
async getUsers() {
try {
// 使用 async/await 语法(更简洁的异步处理,替代 then/catch)
const res = await getUserList({ id: 1 }); // 传入参数
console.log('用户列表:', res.data); // 直接使用后端返回的 data
} catch (error) {
console.error('获取用户列表失败:', error);
}
}

async addNewUser() {
try {
const res = await addUser({ username: 'wangwu', age: 25 });
console.log('新增用户成功:', res.data);
} catch (error) {
console.error('新增用户失败:', error);
}
}

而且在开发环境中,前端项目通常运行在 http://localhost:3000,后端接口运行在 http://localhost:8080,由于浏览器的同源策略,会出现跨域问题。此时需要配置代理来解决,在 vue.config.js 中配置代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
devServer: {
proxy: {
// 匹配以 /api 开头的请求
'/api': {
// 后端接口的基础地址
target: 'http://localhost:8080',
// 是否开启跨域
changeOrigin: true,
// 在这里也可以进行路径重写:把 /api 替换为空(如果后端接口没有 /api 前缀,需要这一步)
// pathRewrite: { '^/api': '' }
}
}
}
};

这样,前端发送的 /api/users 请求会被代理到 http://localhost:8080/api/users,从而解决跨域问题。

这些都是预览,下面会细说

Axios API

基本API及其别名

Axios 内置了很多基本的 API,可以向 axios 传递相关配置来创建请求,但是我们基本上都会封装成实例来用,可以这样进行分类:

  1. 请求方法别名 API:最常用的 axios.get()axios.post() 等,是对核心方法的封装。
  2. 核心请求 APIaxios(config)axios(url, config),是所有请求的底层实现。
  3. 实例相关 APIaxios.create() 创建自定义实例,以及实例上的请求方法、拦截器等。
  4. 辅助 API:处理请求取消、并发请求的工具 API(如 axios.all()axios.CancelToken)。

核心请求 API,是 Axios 最基础的 API,所有其他请求方法都是基于它封装的,灵活性最高。

axios(config),例如

1
2
3
4
5
6
7
8
9
// 发起一个post请求
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});

它有两种的调用形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import axios from 'axios';

// 形式1:只传配置对象
axios({
method: 'get', // 请求方法(必填,默认 get)
url: '/api/users', // 请求地址(必填)
params: { id: 1 }, // GET 请求参数(拼接到 URL)
data: { name: '张三' }, // POST/PUT 请求体参数(仅适用于非 GET 方法)
timeout: 5000, // 请求超时时间(毫秒)
headers: { 'Content-Type': 'application/json' } // 请求头
})
.then(res => console.log(res))
.catch(err => console.error(err));

// 形式2:第一个参数是 URL,第二个是配置对
axios('/api/users', {
method: 'post',
data: { age: 20 }
});

配置对象,也就是axios(config)中的config,是 Axios API 的核心

配置项 类型 说明
url String 请求的服务器 URL(必填)
method String HTTP 请求方法,可选值:get/post/put/delete/head/options 等(默认 get)
params Object/URLSearchParams GET 请求的参数,会被拼接到 URL 后(如 /api/users?id=1
data Object/FormData/String 非 GET 方法的请求体数据,会被发送到后端
headers Object 自定义请求头(如 token、Content-Type)
timeout Number 请求超时时间(毫秒),超过则中断请求(默认无超时)
baseURL String 基础 URL,所有请求的 URL 都会拼接这个前缀(如 baseURL: '/api',则 url: '/users' 最终是 /api/users
responseType String 期望的响应数据类型,可选值:json/blob/document/arraybuffer/text(默认 json,Axios 会自动解析 JSON)
withCredentials Boolean 是否携带跨域 cookies(解决跨域身份验证问题,默认 false)
paramsSerializer Function 自定义 params 序列化规则(比如处理数组、特殊字符)

为了简化开发,Axios 对常用的 HTTP 方法封装了别名 API,不需要手动写 method 等的配置,代码更简洁。

axios.request(config)

axios.get(url[, config])

axios.delete(url[, config])

axios.head(url[, config])

axios.options(url[, config])

axios.post(url[, data[, config]])

axios.put(url[, data[, config]])

axios.patch(url[, data[, config]])

axios.postForm(url[, data[, config]])

axios.putForm(url[, data[, config]])

axios.patchForm(url[, data[, config]])

在使用别名方法时, urlmethoddata 这些属性都不必在配置中指定。

别名方法中,GET/DELETE/HEAD/OPTIONS 这类方法没有 data 参数(因为 HTTP 规范中这些方法的请求体是可选的,后端通常也不处理),参数只能放在 params 或 URL 中;而 POST/PUT/PATCH 可以传 data 作为请求体。

辅助API

  • 并发请求 API:axios.all() + axios.spread()

    用于同时发送多个请求,等待所有请求都完成后再处理结果(比如同时获取用户信息和订单信息)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 定义两个请求
    const request1 = axios.get('/api/user/1');
    const request2 = axios.get('/api/order/1');

    // 并发请求:等待两个请求都完成
    axios.all([request1, request2])
    .then(axios.spread((res1, res2) => {
    // res1 是 request1 的结果,res2 是 request2 的结果
    console.log('用户信息:', res1.data);
    console.log('订单信息:', res2.data);
    }))
    .catch(err => console.error(err));

    在现代 JavaScript 中,axios.all() 可以用 Promise.all() 替代,axios.spread() 可以用解构赋值替代

    1
    2
    3
    4
    Promise.all([request1, request2])
    .then(([res1, res2]) => {
    console.log(res1.data, res2.data);
    });
  • 取消请求 API:AbortController/ CancelToken

    用于中断正在发送的请求(比如用户快速切换标签、输入搜索关键词时取消前一次请求)。

    注意:Axios v0.22.0 及以上版本推荐使用浏览器原生的 AbortControllerCancelToken 已被标记为过时。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 方法1:使用 AbortController(推荐)
    const controller = new AbortController();

    // 发送请求时关联 signal
    axios.get('/api/users', {
    signal: controller.signal // 绑定取消信号
    })
    .then(res => console.log(res))
    .catch(err => {
    // 捕获取消请求的错误
    if (axios.isCancel(err)) {
    console.log('请求被取消:', err.message);
    } else {
    console.error('请求失败:', err);
    }
    });

    // 取消请求(可传入自定义提示信息)
    controller.abort('用户主动取消请求');

    // 方法2:使用 CancelToken(旧版)
    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();

    axios.get('/api/users', {
    cancelToken: source.token
    })
    .catch(err => {
    if (axios.isCancel(err)) {
    console.log('请求被取消:', err.message);
    }
    });

    // 取消请求
    source.cancel('用户主动取消请求');
  • axios.isCancel(error):判断错误是否是取消请求导致的(上面示例已用)。

  • axios.defaults:设置全局默认配置(比如全局 baseURL、timeout):

Axios 实例

创建一个实例

一般情况下,很少直接调用上面的api,使用Axios我们都自定义实例,可以使用自定义配置新建一个实例。这样可以为不同的后端服务配置不同的基础 URL、拦截器等,避免全局配置冲突。

axios.create([config])

它接收一个配置对象作为参数,返回一个新的 Axios 实例

1
2
3
4
5
6
7
8
9
10
11
12
// 创建一个针对用户服务的 Axios 实例
const userService = axios.create({
baseURL: '/api/user', // 基础 URL
timeout: 5000,
headers: { 'Content-Type': 'application/json' }
});

// 创建一个针对订单服务的 Axios 实例
const orderService = axios.create({
baseURL: '/api/order',
timeout: 6000
});

创建的实例和全局的 axios 对象功能完全一致,拥有相同的 API:

  • 别名方法:get()post()put()delete() 等。
  • 拦截器:interceptors.requestinterceptors.response
  • 配置项:可以修改实例的默认配置。

使用Axios实例

实例的请求方法和全局 axios 一致

实例可以直接调用所有请求别名方法,用法和全局 axios 完全相同,只是会使用实例的默认配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 使用 userService 实例发送请求
// 最终请求地址:https://api.example.com/user/list?page=1
userService.get('/list', {
params: { page: 1 }
})
.then(res => console.log('用户列表:', res.data))
.catch(err => console.error(err));

// 使用 orderService 实例发送请求
// 最终请求地址:https://api.example.com/order/create
orderService.post('/create', {
goodsId: 1001,
count: 2
})
.then(res => console.log('订单创建成功:', res.data))
.catch(err => console.error(err));

每个实例可以配置独立的请求拦截器和响应拦截器,实现不同服务的逻辑隔离

  • 请求拦截器,请求发送前处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // 为 userService 实例添加请求拦截器(添加用户 token)
    userService.interceptors.request.use(
    (config) => {
    // 从本地存储获取用户 token,添加到请求头
    const userToken = localStorage.getItem('userToken');
    if (userToken) {
    config.headers['Authorization'] = `Bearer ${userToken}`;
    }
    return config; // 必须返回配置,否则请求会中断
    },
    (error) => {
    // 处理请求配置错误(比如参数错误)
    return Promise.reject(error);
    }
    );

    // 为 orderService 实例添加请求拦截器(添加订单 token + 加载动画)
    orderService.interceptors.request.use(
    (config) => {
    const orderToken = localStorage.getItem('orderToken');
    if (orderToken) {
    config.headers['Order-Token'] = orderToken;
    }
    // 显示加载动画(比如 Element UI 的 Loading)
    // loadingInstance = ElLoading.service({ fullscreen: true });
    return config;
    },
    (error) => {
    // 关闭加载动画
    // loadingInstance.close();
    return Promise.reject(error);
    }
    );
  • 响应拦截器:接收响应后的处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // 为 userService 实例添加响应拦截器(处理用户服务的错误)
    userService.interceptors.response.use(
    (response) => {
    // 成功响应:只返回后端的核心数据(简化数据处理)
    return response.data;
    },
    (error) => {
    // 失败响应:统一处理错误
    if (error.response?.status === 401) {
    // 用户 token 过期,跳转到登录页
    window.location.href = '/user/login';
    alert('用户登录已过期,请重新登录');
    } else if (error.response?.status === 403) {
    alert('您没有访问用户数据的权限');
    }
    return Promise.reject(error);
    }
    );

    // 为 orderService 实例添加响应拦截器(处理订单服务的错误 + 关闭加载动画)
    orderService.interceptors.response.use(
    (response) => {
    // 关闭加载动画
    // loadingInstance.close();
    return response.data;
    },
    (error) => {
    // 关闭加载动画
    // loadingInstance.close();
    if (error.response?.status === 401) {
    // 订单 token 过期,跳转到订单授权页
    window.location.href = '/order/auth';
    alert('订单授权已过期,请重新授权');
    } else if (error.response?.status === 500) {
    alert('订单服务异常,请稍后重试');
    }
    return Promise.reject(error);
    }
    );

拦截器可以动态添加和移除,如果需要动态移除某个拦截器(比如特定场景下不需要拦截逻辑),可以保存拦截器的引用,然后调用 eject 方法:

1
2
3
4
5
// 保存请求拦截器的引用
const userRequestInterceptor = userService.interceptors.request.use(config => config);

// 移除该拦截器
userService.interceptors.request.eject(userRequestInterceptor);

创建实例后,还可以通过 实例.defaults 修改实例的默认配置,优先级低于创建时的配置,但高于单个请求的配置

1
2
3
4
5
// 修改 userService 实例的默认超时时间为 8 秒
userService.defaults.timeout = 8000;

// 为 userService 实例添加默认请求头
userService.defaults.headers.common['X-User-Source'] = 'web';

这里涉及到配置优先级的问题,Axios 的配置可以在全局实例单个请求三个层面设置,它们的优先级是:

单个请求的配置 > 实例的默认配置 > 全局 axios 的默认配置

全局axios的默认值如下

1
2
3
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

在实际项目中,我们会把 Axios 实例的创建、拦截器配置抽离成独立的工具文件,然后再封装接口请求,让代码更规范、易维护

请求配置

下面的官网中官方文档的描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
{
// `url` 是用于请求的服务器 URL
url: '/user',

// `method` 是创建请求时使用的方法
method: 'get', // 默认值

// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',

// `transformRequest` 允许在向服务器发送前,修改请求数据
// 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
// 你可以修改请求头。
transformRequest: [function (data, headers) {
// 对发送的 data 进行任意转换处理

return data;
}],

// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 对接收的 data 进行任意转换处理

return data;
}],

// 自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},

// `params` 是与请求一起发送的 URL 参数
// 必须是一个简单对象或 URLSearchParams 对象
params: {
ID: 12345
},

// `paramsSerializer` 是一个可选配置,允许您自定义序列化 `params`。
paramsSerializer: {

//自定义编码器函数,以迭代方式发送键/值对。
encode?: (param: string): string => { /* 在这里进行自定义操作并返回转换后的字符串 */ },

// 整个参数的自定义序列化器函数。允许用户模仿 1.x 之前的行为。
serialize?: (params: Record<string, any>, options?: ParamsSerializerOptions ),

//用于格式化参数中数组索引的配置。
indexes: false // 三个可用选项:
// (1) indexes: null (导致没有括号),
// (2) (default) indexes: false (导致空括号),
// (3) indexes: true (引导空字符串).
},

// `data` 是作为请求体被发送的数据
// 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法
// 在没有设置 `transformRequest` 时,则必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属: FormData, File, Blob
// - Node 专属: Stream, Buffer
data: {
firstName: 'Fred'
},

// 发送请求体数据的可选语法
// 请求方式 post
// 只有 value 会被发送,key 则不会
data: 'Country=Brasil&City=Belo Horizonte',

// `timeout` 指定请求超时的毫秒数。
// 如果请求时间超过 `timeout` 的值,则请求会被中断
timeout: 1000, // 默认值是 `0` (永不超时)

// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // default

// `adapter` 允许自定义处理请求,这使测试更加容易。
// 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。
adapter: function (config) {
/* ... */
},

// `auth` HTTP Basic Auth
auth: {
username: 'janedoe',
password: 's00pers3cret'
},

// `responseType` 表示浏览器将要响应的数据类型
// 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'
// 浏览器专属:'blob'
responseType: 'json', // 默认值

// `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)
// 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // 默认值

// `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称
xsrfCookieName: 'XSRF-TOKEN', // 默认值

// `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称
xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值

// `onUploadProgress` 允许为上传处理进度事件
// 浏览器专属
onUploadProgress: function (progressEvent) {
// 处理原生进度事件
},

// `onDownloadProgress` 允许为下载处理进度事件
// 浏览器专属
onDownloadProgress: function (progressEvent) {
// 处理原生进度事件
},

// `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数
maxContentLength: 2000,

// `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数
maxBodyLength: 2000,

// `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。
// 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),
// 则promise 将会 resolved,否则是 rejected。
validateStatus: function (status) {
return status >= 200 && status < 300; // 默认值
},

// `maxRedirects` 定义了在node.js中要遵循的最大重定向数。
// 如果设置为0,则不会进行重定向
maxRedirects: 5, // 默认值

// `socketPath` 定义了在node.js中使用的UNIX套接字。
// e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。
// 只能指定 `socketPath` 或 `proxy` 。
// 若都指定,这使用 `socketPath` 。
socketPath: null, // default

// `httpAgent` 和 `httpsAgent` 分别定义了在 node.js 中执行 http 和 https 请求时使用的自定义代理。
// 这允许添加诸如 `keepAlive` 之类的选项,这些选项在 Node.js v19.0.0 之前默认未启用。
// 在 Node.js v19.0.0 之后,不再需要自定义代理来启用 `keepAlive`,
// 因为 `http.globalAgent` 已经默认启用了 `keepAlive`。
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),

// `proxy` 定义了代理服务器的主机名,端口和协议。
// 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。
// 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。
// `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。
// 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。
// 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`
proxy: {
protocol: 'https',
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},

// see https://axios-http.com/zh/docs/cancellation
cancelToken: new CancelToken(function (cancel) {
}),

// `decompress` indicates whether or not the response body should be decompressed
// automatically. If set to `true` will also remove the 'content-encoding' header
// from the responses objects of all decompressed responses
// - Node only (XHR cannot turn off decompression)
decompress: true // 默认值

}

首先明确两个基础规则

  1. 必填项只有 url:所有配置中,只有 url 是必须传的,其他配置都是可选的。
  2. 默认请求方法是 GET:如果不配置 method,请求会默认使用 GET 方法。

基础路径与方法,决定了请求的地址HTTP 方法,是每个请求的基础

配置项 类型 说明 示例
url String 必需,请求的服务器 URL(可以是相对路径或绝对路径) url: '/user'url: 'https://api.example.com/user'
method String 可选,HTTP 请求方法,支持 get/post/put/delete/patch/head/options method: 'post'(默认是 get
baseURL String 可选,自动拼接到 url 前面(除非 url 是绝对路径),用于统一设置接口前缀 baseURL: 'https://api.example.com/api',此时 url: '/user' 最终地址是 https://api.example.com/api/user

这部分在开发的时候,实际上我们都会在创建 Axios 实例时设置 baseURL,避免每个请求都写完整的域名

1
2
3
4
5
const service = axios.create({
baseURL: 'https://api.example.com/api' // 全局前缀
});
service.get('/user'); // 最终请求地址:https://api.example.com/api/user
service.get('https://other-domain.com/user'); // baseURL 会被忽略,因为 url 是绝对路径

数据处理,这类配置允许你在发送请求前修改请求数据,或接收响应后修改响应数据,常用于数据加密、格式化等场景。

配置项 类型 说明 注意事项
transformRequest Array<Function> 可选,发送请求前修改请求数据,仅适用于 PUT/POST/PATCH 方法 最后一个函数必须返回字符串、Buffer、FormData 等可发送的格式;可以修改请求头
transformResponse Array<Function> 可选,接收响应后、传递给 then/catch 前修改响应数据 可以对后端返回的数据进行预处理,比如解密、格式化

例如,数据请求加密就可以这样写

1
2
3
4
5
6
7
8
9
10
11
12
13
axios({
url: '/user/login',
method: 'post',
data: { username: 'admin', password: '123456' },
// 发送前对密码进行加密(这里用简单的 Base64 示例,实际项目用加密算法一般都是 AES)
transformRequest: [function (data, headers) {
// 对 data 进行处理:密码加密
data.password = btoa(data.password); // btoa 是浏览器原生的 Base64 编码
// 转换为 JSON 字符串(因为 Axios 默认发送 JSON 格式)
return JSON.stringify(data);
}],
headers: { 'Content-Type': 'application/json' }
});

参数与请求体相关配置如下,这类配置用于传递URL 参数(GET)和请求体数据(POST/PUT),是前后端数据交互的核心。

配置项 类型 说明 示例
headers Object 可选,自定义请求头,比如设置 Content-Type、token headers: { 'Authorization': 'Bearer token123', 'Content-Type': 'multipart/form-data' }
params Object/URLSearchParams 可选,GET 请求的 URL 参数,会自动拼接到 URL 后 params: { id: 1, page: 1 } → URL 变为 /user?id=1&page=1
paramsSerializer Object/Function 可选,自定义 params 的序列化规则,解决默认序列化的问题(比如数组、特殊字符) 处理数组参数:默认是 arr[]=1&arr[]=2,可以改为 arr=1&arr=2
data Object/String/FormData/Blob 可选,请求体数据,仅适用于 PUT/POST/DELETE/PATCH 可以是 JSON 对象、表单数据、文件流等

注意,Axios 默认对数组的序列化是 arr[]=1&arr[]=2,但有些后端框架不支持这种格式,需要用 paramsSerializer 自定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
axios({
url: '/user/list',
method: 'get',
params: { ids: [1, 2, 3] }, // 数组参数
paramsSerializer: {
// 自定义序列化:把 ids[]=1 转为 ids=1
serialize: (params) => {
return new URLSearchParams(params).toString().replace(/%5B%5D/g, '');
// %5B%5D 是 [] 的 URL 编码,替换为空后,结果是 ids=1&ids=2&ids=3
},
// 另一种配置:indexes: null 可以去掉数组的括号(Axios 内置支持)
indexes: null
}
});

超时与跨域,这类配置用于设置请求的超时时间、跨域是否携带凭证等,解决请求的基础问题。

配置项 类型 说明 示例
timeout Number 可选,请求超时时间(毫秒),超过则中断请求 timeout: 5000(5 秒超时,默认 0 表示永不超时)
withCredentials Boolean 可选,跨域请求时是否携带 cookies / 认证信息 withCredentials: true(解决跨域身份验证问题,需要后端配合设置 CORS)

例如,跨域携带 cookies

1
2
3
4
5
axios({
url: 'https://other-domain.com/api/user',
method: 'get',
withCredentials: true // 跨域请求携带 cookies
});

此时后端需要设置 Access-Control-Allow-Origin 为前端域名,且 Access-Control-Allow-Credentials: true,否则浏览器会阻止。

响应处理,这类配置决定了 Axios 如何解析响应数据、判断请求是否成功,以及限制响应数据的大小。

配置项 类型 说明 示例
responseType String 可选,期望的响应数据类型,默认 json 可选值:json(默认,自动解析 JSON)、blob(文件流)、text(纯文本)、arraybuffer(二进制数据)
responseEncoding String 可选,解码响应的编码(仅 Node.js 专属) responseEncoding: 'utf8'(默认)
maxContentLength Number 可选,Node.js 中允许的响应内容最大字节数 maxContentLength: 1024 * 1024(1MB)
maxBodyLength Number 可选,Node.js 中允许的请求内容最大字节数 maxBodyLength: 1024 * 1024(1MB)
validateStatus Function 可选,定义 HTTP 状态码是否视为成功(resolve promise) 默认是 status >= 200 && status < 300,可以自定义(比如允许 304 状态码)

例如,下载文件(responseType: ‘blob’)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
axios({
url: '/file/download',
method: 'get',
responseType: 'blob', // 响应为二进制文件流
params: { fileId: 1 }
}).then(res => {
// 创建下载链接
const url = window.URL.createObjectURL(new Blob([res]));
const a = document.createElement('a');
a.href = url;
a.download = '文件名称.xlsx';
a.click();
window.URL.revokeObjectURL(url); // 释放资源
});

进度与安全,这类配置用于处理上传 / 下载的进度事件,以及防御跨站请求伪造(CSRF)。

配置项 类型 说明 实战场景
onUploadProgress Function 可选,上传进度事件(仅浏览器专属) 显示文件上传的进度条
onDownloadProgress Function 可选,下载进度事件(仅浏览器专属) 显示文件下载的进度条
xsrfCookieName String 可选,CSRF token 的 cookie 名称,默认 XSRF-TOKEN 防御 CSRF 攻击,Axios 会自动从 cookie 中读取该值,添加到请求头
xsrfHeaderName String 可选,携带 CSRF token 的请求头名称,默认 X-XSRF-TOKEN

例如,文件上传进度条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 选择文件的 input 元素
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);

axios({
url: '/file/upload',
method: 'post',
data: formData,
headers: { 'Content-Type': 'multipart/form-data' },
// 监听上传进度
onUploadProgress: function (progressEvent) {
// progressEvent.loaded 是已上传的字节数,progressEvent.total 是总字节数
const progress = (progressEvent.loaded / progressEvent.total) * 100;
console.log(`上传进度:${progress.toFixed(2)}%`);
// 把进度赋值给进度条组件
// progressBar.value = progress;
}
});

代理、取消请求、重定向等其他配置

配置项 类型 说明 实战场景
auth Object 可选,HTTP 基础认证(Basic Auth) 访问需要用户名密码的接口,Axios 会自动添加 Authorization
cancelToken CancelToken/AbortSignal 可选,取消请求的令牌(新版用 AbortSignal,旧版用 CancelToken) 取消正在发送的请求(比如用户快速切换标签)
proxy Object/Boolean 可选,代理服务器配置(仅 Node.js 专属) Node.js 中通过代理发送请求,比如访问外网接口
maxRedirects Number 可选,Node.js 中允许的最大重定向数,默认 5 限制请求的重定向次数,避免无限重定向
socketPath String 可选,Node.js 中使用的 UNIX 套接字 连接 Docker 守护进程等本地服务
httpAgent/httpsAgent Object 可选,Node.js 中自定义 HTTP/HTTPS 代理 设置长连接(keepAlive)等选项
decompress Boolean 可选,是否自动解压响应体(仅 Node.js 专属) 默认 true,关闭则不解压
adapter Function 可选,自定义请求适配器 测试时模拟请求,或自定义请求处理逻辑

响应结构

当 Axios 发送请求并成功收到后端的响应后,会返回一个响应对象(Response),这个对象包含了后端返回的所有信息,以及请求的配置和底层请求对象。

首先:

  1. 响应对象是 then 回调的唯一参数(请求成功时)。
  2. 所有响应头的名称都会被自动转为小写(比如 Content-Type 变成 content-type)。
  3. 即使在响应拦截器中修改了响应数据,最终拿到的对象依然保留这些核心属性(或被拦截器简化)。

而且,一个请求的响应包含以下信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
// `data` 由服务器提供的响应
data: {},

// `status` 来自服务器响应的 HTTP 状态码
status: 200,

// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',

// `headers` 是服务器响应头
// 所有的 header 名称都是小写,而且可以使用方括号语法访问
// 例如: `response.headers['content-type']`
headers: {},

// `config` 是 `axios` 请求的配置信息
config: {},

// `request` 是生成此响应的请求
// 在node.js中它是最后一个ClientRequest实例 (in redirects),
// 在浏览器中则是 XMLHttpRequest 实例
request: {}
}
  1. data:服务器返回的核心数据

    • 类型:任意类型,因为是后端接口返回的实际数据,取决于 responseType 配置

    • 如果 responseType 是默认的 json,Axios 会自动将后端返回的 JSON 字符串解析为 JavaScript 对象 / 数组

      1
      2
      3
      4
      5
      6
      7
      8
      axios.get('/api/user/1')
      .then(response => {
      // 后端返回的数据:{ "code": 200, "data": { "name": "张三", "age": 20 }, "msg": "成功" }
      console.log(response.data); // 解析后的对象:{ code: 200, data: { name: '张三', age: 20 }, msg: '成功' }
      // 提取用户信息
      const userInfo = response.data.data;
      console.log(userInfo.name); // 张三
      });
  2. status:HTTP 状态码

    • 类型:Number。

    • 含义:服务器返回的 HTTP 状态码,代表请求的处理结果

    • 例如,根据状态码做特殊处理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      axios.post('/api/user', { name: '李四' })
      .then(response => {
      if (response.status === 201) {
      console.log('用户新增成功(HTTP 201)');
      } else if (response.status === 200) {
      console.log('用户新增成功(HTTP 200)');
      }
      console.log(response.data);
      });
  3. statusText:HTTP 状态信息

    • 类型:String。
    • 含义:服务器返回的 HTTP 状态文本,与 status 一一对应(如 200 对应 OK,404 对应 Not Found)。
    • 注意:状态文本是 HTTP 协议规定的
  4. headers:服务器响应头

    • 类型:Object。
    • 含义:服务器返回的响应头信息,所有头名称都会被转为小写
    • 常用响应头
      • content-type:响应数据的类型(如 application/jsontext/htmlmultipart/form-data)。
      • content-length:响应数据的字节数。
      • cache-control:缓存控制策略(如 no-cachemax-age=3600)。
      • authorization:认证令牌(少数后端会在响应头返回 token)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    axios.get('/api/user/1')
    .then(response => {
    // 获取响应数据的类型
    console.log(response.headers['content-type']); // application/json; charset=utf-8
    // 获取缓存控制策略
    console.log(response.headers['cache-control']); // no-cache
    // 注意:头名称是小写,不能写 Content-Type
    console.log(response.headers['Content-Type']); // undefined
    });
    • 注意,跨域请求中,在浏览器中,跨域请求时只能获取简单响应头(如 content-typecache-control),如果需要获取自定义响应头(如 x-token),后端需要设置 Access-Control-Expose-Headers 头,指定允许前端访问的自定义头。
  5. config:请求的配置对象

    • 类型:Object。

    • 含义:发送请求时使用的所有配置信息(包括默认配置、实例配置、单个请求配置)。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      axios.get('/api/user/1', {
      params: { id: 1 },
      timeout: 5000
      })
      .then(response => {
      console.log(response.config.url); // /api/user/1
      console.log(response.config.method); // get
      console.log(response.config.params); // { id: 1 }
      console.log(response.config.timeout); // 5000
      });
  6. request:生成响应的请求对象

    • 类型
      • 浏览器环境:XMLHttpRequest 实例(原生 AJAX 对象)。
      • Node.js 环境:ClientRequest 实例(Node.js 内置的 HTTP 请求对象)。
    • 含义:底层的请求对象,通常在开发中不需要使用,主要用于调试或特殊场景(比如获取请求的响应时间)。

在实际中,我们通常会在响应拦截器中对响应对象进行处理(比如只返回 data,简化后续代码),这是对响应对象最常见的实战用法。

例如,简化响应数据,只返回 data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import axios from 'axios';

const service = axios.create({
baseURL: '/api',
timeout: 5000
});

// 响应拦截器
service.interceptors.response.use(
(response) => {
// 只返回后端的核心数据(response.data),后续 then 中直接拿到的就是这个值
return response.data;
},
(error) => {
return Promise.reject(error);
}
);

// 使用时,直接拿到的是 response.data,不需要再写 response.data
service.get('/user/1')
.then(data => {
console.log(data); // 等价于原来的 response.data
console.log(data.data.name); // 张三
});

当请求失败时(比如网络错误、HTTP 状态码非 2xx),错误对象中也会包含响应对象(error.response),这是处理错误的关键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
service.interceptors.response.use(
(response) => response.data,
(error) => {
let errorMsg = '请求失败';
if (error.response) {
// 服务器返回错误
const { status, data } = error.response;
switch (status) {
case 401:
errorMsg = '登录已过期,请重新登录';
// 跳转到登录页
window.location.href = '/login';
break;
case 403:
errorMsg = '您没有访问权限';
break;
case 404:
errorMsg = '请求的资源不存在';
break;
case 500:
errorMsg = '服务器内部错误,请稍后重试';
break;
default:
errorMsg = data.msg || '请求失败';
}
} else if (error.request) {
// 网络错误
errorMsg = '网络错误,请检查网络连接';
} else {
// 配置错误
errorMsg = error.message;
}
// 提示错误信息(比如用 Element UI 的 ElMessage)
// ElMessage.error(errorMsg);
console.error(errorMsg);
return Promise.reject(error);
}
);

responseType 配置为非 json 时(如 blobtextarraybuffer),data 属性的类型会对应变化,这是处理文件下载、纯文本响应的关键。

例如,处理文件下载,也就是responseType: 'blob'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 下载 Excel 文件
axios.get('/api/file/download/1', {
responseType: 'blob' // 响应数据为二进制流
})
.then(response => {
// response.data 是 Blob 对象
const blob = response.data;
// 创建下载链接
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
// 从响应头中获取文件名(需要后端配合设置)
const fileName = response.headers['content-disposition']?.split('filename=')[1] || '文件.xlsx';
a.download = decodeURIComponent(fileName); // 解码中文文件名
a.click();
// 释放资源
window.URL.revokeObjectURL(url);
});

错误处理

首先要明确:Axios 在请求过程中产生的错误,主要分为三大类

错误类型 产生原因 特征(如何判断)
服务器响应错误 请求发送成功,服务器返回了状态码,但状态码不在 2xx 范围内(默认规则) error.response 存在(非 undefined)
无响应错误 请求发送成功,但没有收到服务器的响应(如网络断开、请求超时、服务器宕机) error.request 存在,error.response 不存在
请求配置 / 代码错误 发送请求前就出现了错误(如 URL 写错、数据格式错误、Axios 配置错误) error.message 存在,error.request 不存在

除此之外,还有一个兜底的属性error.config,它在所有错误类型中都存在,代表发送请求时的配置对象,可用于调试或重新发起请求。

服务器响应错误(error.response 存在)

这是最常见的错误类型,比如后端返回 401(未授权)、404(资源不存在)、500(服务器内部错误)等。

此时,

  • 请求已经到达服务器,服务器处理后返回了 HTTP 状态码,但状态码不在 Axios 默认的成功范围内(200 <= status < 300)。
  • error.response 包含完整的响应信息:data(后端返回的错误数据)、status(状态码)、headers(响应头)。
1
2
3
4
5
6
7
8
9
10
11
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// 打印后端返回的错误数据(比如 { code: 404, msg: '用户不存在' })
console.log(error.response.data);
// 打印状态码(如 404、500)
console.log(error.response.status);
// 打印响应头(如 content-type)
console.log(error.response.headers);
}
});

第二类:无响应错误(error.request 存在)

这类错误是网络层面的问题,请求发出去了,但服务器没有任何回应。

这种情况一般就是网络断开、请求超时(timeout 配置触发)、服务器宕机、跨域配置错误导致浏览器拦截响应。

1
2
3
4
5
6
7
8
9
10
11
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// 服务器响应错误逻辑
} else if (error.request) {
// 打印请求对象(用于调试,比如查看请求的 URL、方法)
console.log(error.request);
// 统一提示网络错误
alert('网络错误,请检查网络连接或稍后重试');
}
});

请求超时也属于这类错误!比如配置了 timeout: 5000,如果请求超过 5 秒还没收到响应,就会触发 error.request

请求配置 / 代码错误(error.message 存在)

这类错误是在发送请求之前就发生的,属于前端代码或配置的问题。

此时错误信息会被存储在 error.message 中,描述错误的具体原因

1
2
3
4
5
6
7
8
9
10
11
12
13
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// 服务器响应错误
} else if (error.request) {
// 无响应错误
} else {
// 打印错误信息(如 "Invalid URL"、"Cannot read properties of undefined")
console.log('Error', error.message);
// 提示前端代码错误
alert('请求配置错误:' + error.message);
}
});

无论哪种错误,error.config 都存在,它是发送请求时的完整配置对象(包含 URL、method、params、data、timeout 等)。

自定义错误判定规则:validateStatus

Axios 默认的规则是:只有状态码在 200 <= status < 300 范围内,才会触发 then;否则触发 catch。但我们可以通过 validateStatus 配置项自定义这个规则,决定哪些状态码算 “成功”,哪些算 “错误”。

validateStatus 的基本用法

validateStatus 是一个函数,接收 status(HTTP 状态码)作为参数,返回一个布尔值:

  • 返回 true:状态码被视为成功,触发 then
  • 返回 false:状态码被视为错误,触发 catch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
axios.get('/user/12345', {
// 自定义规则:状态码小于 500 都视为成功(即只有 5xx 才触发 catch)
validateStatus: function (status) {
return status < 500; // 2xx、3xx、4xx 都算成功
}
})
.then(function (response) {
// 即使是 404、401,也会进入这里
console.log(response.status); // 比如 404
})
.catch(function (error) {
// 只有 5xx 状态码才会进入这里
console.log(error.response.status); // 比如 500
});

304 状态码表示 “资源未修改,使用缓存”,默认属于成功,但如果需要特殊处理,可以通过 validateStatus 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
axios.get('/user/12345', {
// 规则:2xx 或 304 都算成功
validateStatus: function (status) {
return (status >= 200 && status < 300) || status === 304;
}
})
.then(function (response) {
if (response.status === 304) {
console.log('资源未修改,使用缓存');
} else {
console.log('请求成功');
}
});

神秘小知识,这样写,全部状态码都触发 then

1
2
3
4
5
6
7
axios.get('/user/12345', {
validateStatus: () => true
})
.then(function (response) {
// 即使是 500,也会进入这里
console.log(response.status); // 500
});

全局统一错误处理

在实际项目中,我们不会在每个请求的 catch 中重复写错误处理逻辑,而是通过Axios 拦截器实现全局统一的错误处理,这是最高效的方案。

首先创建 Axios 实例并配置拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import axios from 'axios';

// 创建实例
const service = axios.create({
baseURL: '/api',
timeout: 5000
});

// 响应拦截器:统一处理错误
service.interceptors.response.use(
// 请求成功:简化返回数据(只返回 response.data)
(response) => {
return response.data;
},
// 请求失败:统一处理错误
(error) => {
let errorMsg = '';
// 1. 服务器响应错误
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 401:
errorMsg = '登录已过期,请重新登录';
// 跳转到登录页(可配合路由守卫)
window.location.href = '/login';
break;
case 403:
errorMsg = '您没有访问该资源的权限';
break;
case 404:
errorMsg = data.msg || '请求的资源不存在';
break;
case 400:
errorMsg = data.msg || '参数错误,请检查输入';
break;
case 500:
errorMsg = '服务器内部错误,请稍后重试';
break;
default:
errorMsg = data.msg || `请求失败(状态码:${status})`;
}
}
// 2. 无响应错误(网络/超时)
else if (error.request) {
// 判断是否是超时错误(error.code 为 "ECONNABORTED")
if (error.code === 'ECONNABORTED') {
errorMsg = '请求超时,请稍后重试';
} else {
errorMsg = '网络错误,请检查网络连接';
}
}
// 3. 配置/代码错误
else {
errorMsg = `请求配置错误:${error.message}`;
}

// 提示错误信息(可替换为 UI 组件库的提示,如 Element Plus 的 ElMessage)
console.error(errorMsg);
// ElMessage.error(errorMsg);

// 继续抛出错误,让业务代码可以进一步处理
return Promise.reject(error);
}
);

export default service;

这样,在业务代码中使用就很简单高效了

1
2
3
4
5
6
7
8
9
10
11
12
13
import service from '@/utils/request';

// 获取用户信息
async function getUserInfo() {
try {
const data = await service.get('/user/12345');
// 只处理成功逻辑,错误由全局拦截器处理
console.log('用户信息:', data);
} catch (error) {
// 可选:业务代码中需要特殊处理的错误(覆盖全局逻辑)
console.log('业务特殊错误处理:', error);
}
}

注意,取消请求AbortControllerCancelToken),也会触发 catch,这类错误有一个特殊的判断方式:axios.isCancel(error)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import axios from 'axios';

const controller = new AbortController();

axios.get('/user/12345', {
signal: controller.signal
})
.catch(function (error) {
// 判断是否是取消请求的错误
if (axios.isCancel(error)) {
console.log('请求被取消:', error.message);
} else {
// 处理普通错误
console.error('请求失败:', error);
}
});

// 取消请求
controller.abort('用户主动取消请求');

取消请求

理解请求的取消

v0.22.0 开始,Axios 支持以 fetch API 方式—— AbortController 取消请求

这是个什么 API

在流式请求中我们可能知道取消请求,这个很常用,但是实际上,相比取消,流式请求的”取消”更像是“打断”,因为它的请求到服务器了,只不过是在响应输出的过程中中止了,那么这个取消是什么意思呢?

流式请求的 “取消” 是客户端 + 传输层 + 服务器层的协同行为,但不同层面的 “取消” 效果是不一样的:

  • 客户端:AbortController调用abort()立刻生效的行为,也是我们最直观感受到的 “取消”,也就是客户端不收数据了

  • 传输层层面(TCP 的 “取消”:断开连接):当客户端触发取消后,底层的 TCP 协议会做出响应

    • 如果是HTTP/1.1:客户端会向服务器发送FIN包,关闭 TCP 连接(或重置连接RST)。服务器在尝试继续发送数据时,会发现连接已断开,从而停止传输。
    • 如果是HTTP/2:支持流的单独取消(HTTP/2 的多路复用特性),客户端会发送RST_STREAM帧,只终止当前的流式请求流,不会影响同连接的其他请求。

    这一步是客户端取消后,传输层自动完成的,相当于 “切断了客户端和服务器之间的数据线”。

  • 服务器:这是最关键的一层,也是很多人误以为 “取消没用” 的原因 ——默认情况下,服务器不会因为客户端取消连接而停止处理请求。服务器需要主动做适配处理,才能实现 “真正的取消(停止处理)”。

普通请求中的取消,也就是使用AbortController,是差不多的

当你调用controller.abort()时,客户端会立刻执行以下操作:

  • 终止请求实例:Axios/fetch 的请求对象会立刻停止后续的所有行为,不再等待服务器的响应。
  • 触发取消错误:请求的 Promise 会被拒绝,抛出可识别的取消错误(Axios 中可通过axios.isCancel()判断)。
  • 终止前端业务逻辑:比如取消后,前端不会再执行请求成功后的渲染、数据处理等逻辑。

简单说:客户端直接放弃了这个请求的 “结果等待”,无论服务器后续是否返回数据,客户端都不会处理了

普通请求的取消在传输层的行为,取决于你在哪个阶段调用了abort(),这是和流式请求最大的不同点:

请求阶段 传输层行为
请求还未发送完成 TCP 连接会发送RST(重置)包,直接中断请求的发送,服务器甚至可能没收到完整的请求。
请求已发送,服务器未响应 TCP 连接会关闭,服务器后续返回的响应数据会因为连接断开而无法到达客户端。
服务器已开始返回响应 客户端会停止接收剩余的响应数据(即使响应还没传完),直接终止连接。

而和流式请求一样,服务器层面,默认情况下,服务器不会因为客户端的取消而停止处理请求

AbortController

AbortController是浏览器(也兼容 Node.js 环境)提供的一个原生 API,作用是主动终止一个或多个正在进行的异步操作(比如网络请求、数据流处理等)。

通常情况下,用到取消请求的位置比较多

AbortController构造函数:创建一个控制器实例,实例包含:

  • signal属性:一个AbortSignal对象,用于传递取消信号,可绑定到异步操作上。
  • abort()方法:调用该方法时,会触发signalabort事件,并将signal.aborted设为true,通知绑定的异步操作取消。

AbortSignal对象:作为 “信号载体”,异步操作会监听这个对象的状态变化,一旦收到取消信号就停止执行。

简单来说,AbortController的工作逻辑是创建控制器 → 把信号绑定到异步操作 → 调用abort()方法触发取消

普通 Axios 请求中取消请求

  • AbortControllersignal传入请求配置中,需要取消时调用abort()方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // 1. 导入Axios
    import axios from 'axios';

    // 2. 创建AbortController实例
    const controller = new AbortController();
    const { signal } = controller;

    // 3. 发送Axios请求,将signal传入配置
    const fetchData = async () => {
    try {
    const response = await axios.get('https://api.example.com/data', {
    signal: signal, // 绑定取消信号
    });
    console.log('请求成功:', response.data);
    } catch (error) {
    // 捕获取消请求的错误,区分普通错误和取消错误
    if (axios.isCancel(error)) {
    console.log('请求被取消:', error.message);
    } else {
    console.log('请求失败:', error.message);
    }
    }
    };

    // 4. 调用请求
    fetchData();

    // 5. 手动触发取消(比如点击按钮、定时器等场景)
    // 示例:3秒后取消请求
    setTimeout(() => {
    controller.abort('用户主动取消请求'); // 可传入自定义取消信息
    }, 3000);
  • 如果需要同时取消多个请求,只需将同一个signal传入所有请求的配置中,调用一次abort()即可批量取消:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const controller = new AbortController();
    const { signal } = controller;

    // 请求1
    axios.get('https://api.example.com/data1', { signal })
    .catch(err => axios.isCancel(err) && console.log('请求1被取消'));

    // 请求2
    axios.post('https://api.example.com/data2', { name: 'test' }, { signal })
    .catch(err => axios.isCancel(err) && console.log('请求2被取消'));

    // 批量取消
    setTimeout(() => {
    controller.abort('批量取消所有请求');
    }, 2000);

流式请求(比如后端返回流式数据,如大文件下载、实时日志推送、AI 对话流式响应)的取消,同样依赖AbortController,但需要结合流式响应的处理逻辑(如ReadableStream)来实现。

以 AI 对话流式响应为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import axios from 'axios';

// 创建AbortController实例
const controller = new AbortController();
const { signal } = controller;

// 发送流式请求(注意配置responseType为stream,且处理流式数据)
const streamRequest = async () => {
try {
const response = await axios.post(
'https://api.example.com/stream/chat',
{ prompt: '你好' },
{
signal: signal, // 绑定取消信号
responseType: 'stream', // 关键:指定响应类型为流
headers: {
'Content-Type': 'application/json',
},
}
);

// 处理流式响应(以Node.js或浏览器的ReadableStream为例)
const stream = response.data;

// 浏览器环境:处理ReadableStream
if (stream.getReader) {
const reader = stream.getReader();
const decoder = new TextDecoder();

// 循环读取流数据
const readStream = async () => {
try {
const { done, value } = await reader.read();
if (done) {
console.log('流式请求完成');
return;
}
// 处理每一段数据(比如AI的逐字响应)
const chunk = decoder.decode(value, { stream: true });
console.log('收到流式数据:', chunk);
// 继续读取下一段
await readStream();
} catch (err) {
if (err.name === 'AbortError') {
console.log('流式请求被取消');
} else {
console.error('读取流失败:', err);
}
}
};

await readStream();
}
} catch (error) {
if (axios.isCancel(error)) {
console.log('流式请求被主动取消:', error.message);
} else {
console.error('流式请求失败:', error.message);
}
}
};

// 执行流式请求
streamRequest();

// 手动取消流式请求(比如用户点击“停止”按钮)
setTimeout(() => {
controller.abort('用户停止流式响应');
}, 5000);
  • 即使调用了abort(),已经读取的流式数据不会被回滚,只会停止后续数据的读取。

请求体编码

请求体编码的核心是将 JavaScript 对象转换为特定格式的字符串(或二进制数据),并通过Content-Type请求头告诉服务器 “该如何解析这些数据”

Axios 作为 HTTP 客户端,会根据你传入的data类型和Content-Type头,自动或手动处理编码逻辑,常见的编码格式有 3 种:

  1. application/json:Axios 默认格式,将对象序列化为 JSON 字符串。
  2. application/x-www-form-urlencoded:表单默认格式,将对象序列化为key1=value1&key2=value2的键值对字符串。
  3. multipart/form-data:用于上传文件 / 二进制数据,将数据拆分为多个部分传输。

默认编码application/json

当你向 Axios 传入普通 JavaScript 对象作为data时,Axios 会自动:

  • 将对象序列化为JSON 字符串(等价于JSON.stringify(data))。

  • 设置请求头Content-Type: application/json;charset=utf-8

  • 服务器端需要用 JSON 解析器(如 Express 的express.json())来解析请求体。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import axios from 'axios';

    // 普通对象作为data
    const data = {
    name: '张三',
    age: 20,
    hobbies: ['篮球', '游戏']
    };

    // Axios自动序列化为JSON,设置Content-Type
    axios.post('/api/user', data)
    .then(res => console.log(res))
    .catch(err => console.log(err));

application/x-www-form-urlencoded 编码(表单键值对)

这种格式是传统 HTML 表单的默认提交格式,特点是数据为key=value的键值对,多个键值对用&分隔。Axios 不会自动处理这种编码

在浏览器环境下,我们一般的实现方式是使用 qs 库,npm install qs --save

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import qs from 'qs';

// 复杂数据(包含嵌套对象、数组)
const data = {
name: '张三',
age: 20,
info: {
address: '北京',
phone: '123456'
},
hobbies: ['篮球', '游戏']
};

// 序列化数据
const encodedData = qs.stringify(data);
// 手动设置Content-Type(也可以省略,Axios会根据数据格式自动识别)
axios.post('/api/form', encodedData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(res => console.log(res));

使用原生URLSearchParams也比较常见,我不习惯这么写

1
2
3
4
5
6
7
8
9
10
// 创建URLSearchParams实例,添加键值对
const params = new URLSearchParams();
params.append('name', '张三');
params.append('age', '20');
params.append('hobbies', '篮球'); // 数组需要多次append
params.append('hobbies', '游戏');

// Axios会自动设置Content-Type为application/x-www-form-urlencoded
axios.post('/api/form', params)
.then(res => console.log(res));

Node.js 下,使用原生querystring模块,这个不支持嵌套对象,基本不用,使用url模块的URLSearchParams是更常见的,但是它好像对嵌套对象支持是有限的

1
2
3
4
5
6
7
8
9
const axios = require('axios');
const url = require('url');

const params = new url.URLSearchParams({ name: '张三', age: 20 });
// 转换为字符串
const encodedData = params.toString();

axios.post('http://localhost:3000/api/form', encodedData)
.then(res => console.log(res));

Node.js 下也可以使用qs库,它完全兼容浏览器环境的用法,支持嵌套对象,是 Node.js 环境处理复杂数据的首选。

说一下Axios 新特性,自动序列化,Axios 在新版本中提供了 自动序列化application/x-www-form-urlencoded 的功能,无需手动调用qsURLSearchParams,只需设置Content-Type头即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import axios from 'axios';

// 复杂数据(包含数组、嵌套对象)
const data = {
x: 1,
arr: [1, 2, 3],
users: [{ name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }]
};

// 只需设置Content-Type,Axios自动序列化
axios.post('https://postman-echo.com/post', data, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(res => {
console.log(res.data);
});

Axios 会将复杂数据转换为嵌套键值对格式,例如:

  • 数组arr: [1,2,3]arr[]=1&arr[]=2&arr[]=3
  • 嵌套对象users[0].nameusers[0][name]=Peter
  • 多维数组arr2: [1, [2], 3]arr2[0]=1&arr2[1][0]=2&arr2[2]=3

服务器需要开启嵌套对象解析功能

multipart/form-data 编码(文件上传)

这种格式用于传输二进制数据(如图片、视频、文件)或混合数据(文本 + 文件),常见于文件上传场景。

浏览器中可以直接使用原生FormData对象,Axios 会自动设置Content-Type: multipart/form-data

1
2
3
4
5
6
7
8
9
10
11
// 创建FormData实例
const formData = new FormData();
// 添加普通文本数据
formData.append('name', '张三');
// 添加文件(从<input type="file">获取)
const fileInput = document.querySelector('input[type="file"]');
formData.append('avatar', fileInput.files[0]);

// 发送请求,Axios自动处理编码和请求头
axios.post('/api/upload', formData)
.then(res => console.log(res));
  • 如果频繁使用 FormData,可以添加 Axios 拦截器,避免每次手动设置头:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    axios.interceptors.request.use(config => {
    // 如果data是FormData实例,自动设置请求头
    if (config.data instanceof FormData) {
    Object.assign(config.headers, config.data.getHeaders());
    }
    return config;
    });

    // 之后发送FormData请求时,无需手动设置headers
    axios.post('/api/upload', form).then(res => console.log(res));

Node.js 中没有原生FormData,需要使用form-data第三方库。

Multipart 实体请求

multipart/form-data是一种用于传输复杂数据(文本、二进制文件、Blob 等)的 HTTP 请求体编码格式,核心特点是:

  • 数据被拆分为多个 “部分(part)”,每个部分有独立的标识(boundary 分隔符)。
  • 支持同时传输文本数据和二进制数据(如图片、视频、文件),这是application/x-www-form-urlencoded无法做到的。
  • Axios 会根据传入的data类型(如 FormData 对象)或请求头Content-Type: multipart/form-data,自动处理数据的序列化和请求头设置。

原生 FormData API

这是最传统的用法,直接使用FormData对象组装数据,Axios 会识别并自动处理请求头和序列化

上面讲了

只不过,浏览器的FormData无需手动设置请求头,Axios 会自动完成。

Axios 简化用法:postForm/putForm/patchForm别名方法

为了简化开发,Axios 提供了专门用于multipart/form-data的请求别名方法,这些方法的核心是:

  • 自动设置请求头Content-Type: multipart/form-data
  • 支持直接传入普通对象、FileList、Blob 等,无需手动创建 FormData。

浏览器

1
2
3
4
5
6
7
8
9
10
// 替代axios.post + 手动设置Content-Type
axios.postForm('https://httpbin.org/post', {
my_field: 'my value', // 普通文本
my_buffer: new Blob([1, 2, 3]), // Blob数据
my_file: document.querySelector('input[type="file"]').files[0] // 单文件
});

// 同理,put/patch请求有对应的putForm/patchForm
axios.putForm('https://httpbin.org/put', { key: 'value' });
axios.patchForm('https://httpbin.org/patch', { key: 'value' });

直接传递 FileList(多文件上传)

这是非常实用的功能,可直接将<input type="file" multiple>files对象传入,Axios 会自动处理为多文件:

1
2
3
4
5
6
7
// 场景1:直接传FileList,Axios会用字段名files[]发送所有文件
await axios.postForm('https://httpbin.org/post', document.querySelector('#fileInput').files);

// 场景2:自定义字段名,传入FileList(用[]结尾表示多文件)
await axios.postForm('https://httpbin.org/post', {
'images[]': document.querySelector('#fileInput').files // 所有文件以images[]为字段名
});

所有文件会以files[](或images[])为字段名,逐个添加到 FormData 中,实现多文件上传。

Axios 的multipart/form-data自动序列化

当请求头设置为Content-Type: multipart/form-data时,Axios 会自动将普通 JavaScript 对象序列化为 FormData 对象,无需手动创建 FormData。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import axios from 'axios';

// 普通对象(包含文本、嵌套对象、文件)
const data = {
user: { name: 'Dmitriy' }, // 嵌套对象
age: 20, // 普通数值
file: document.querySelector('#fileInput').files[0] // 文件(浏览器)
// file: fs.createReadStream('/foo/bar.jpg') // Node.js文件流
};

// 发送请求:设置Content-Type后,Axios自动序列化为FormData
axios.post('https://httpbin.org/post', data, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(({ data }) => console.log(data));

Axios 支持通过特殊的键名后缀来定制序列化规则,解决复杂数据的处理问题:

后缀 作用 示例 序列化结果
{} 将值通过JSON.stringify序列化为 JSON 字符串 'myObj{}': {x: 1, s: "foo"} myObj{}: "{\"x\":1,\"s\":\"foo\"}"
[] 将类数组对象(如 FileList)展开为多个同名字段 'files[]': fileInput.files files[]: file1, files[]: file2

示例代码

1
2
3
4
5
6
7
8
axios.post('https://httpbin.org/post', {
'myObj{}': { x: 1, s: "foo" }, // 嵌套对象→JSON字符串
'files[]': document.querySelector('#fileInput').files // FileList→多文件
}, {
headers: {
'Content-Type': 'multipart/form-data'
}
});

对于包含数组、嵌套对象的普通对象,Axios 会按照括号语法自动拆解为 FormData 的键值对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 原始对象
const obj = {
x: 1,
arr: [1, 2, 3],
users: [{ name: 'Peter' }, { name: 'Thomas' }],
'obj2{}': [{ x: 1 }]
};

// Axios自动序列化后的FormData等价于:
const formData = new FormData();
formData.append('x', '1');
formData.append('arr[]', '1'); // 数组→arr[]+多个值
formData.append('arr[]', '2');
formData.append('arr[]', '3');
formData.append('users[0][name]', 'Peter'); // 嵌套对象→括号索引
formData.append('users[1][name]', 'Thomas');
formData.append('obj2{}', '[{"x":1}]'); // {}后缀→JSON字符串