axios 封装

本文最后更新于:2024年6月3日 早上

不管是在 vue 项目还是 react 项目中,前端常用的 request 请求库都是 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
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import axios from "axios";
import { ElLoading, ElMessage } from "element-plus";
// 获取token,如果你的项目有其他获取toke的方式请替换这里
import { getTokenAUTH } from "@/utils/auth";

const pendingMap = new Map();

const LoadingInstance = {
_target: null,
_count: 0,
};

function myAxios(axiosConfig, customOptions, loadingOptions) {
const service = axios.create({
baseURL: "http://localhost:8888", // 设置统一的请求前缀
timeout: 10000, // 设置统一的超时时长
});

// 自定义配置
let custom_options = Object.assign(
{
repeat_request_cancel: true, // 是否开启取消重复请求, 默认为 true
loading: false, // 是否开启loading层效果, 默认为false
reduct_data_format: true, // 是否开启简洁的数据结构响应, 默认为true
error_message_show: true, // 是否开启接口错误信息展示,默认为true
code_message_show: false, // 是否开启code不为0时的信息提示, 默认为false
},
customOptions
);

// 请求拦截
service.interceptors.request.use(
(config) => {
removePending(config);
custom_options.repeat_request_cancel && addPending(config);
// 创建loading实例
if (custom_options.loading) {
LoadingInstance._count++;
if (LoadingInstance._count === 1) {
LoadingInstance._target = ElLoading.service(loadingOptions);
}
}
// 自动携带token
if (getTokenAUTH() && typeof window !== "undefined") {
config.headers.Authorization = getTokenAUTH();
}

return config;
},
(error) => {
return Promise.reject(error);
}
);

// 响应拦截
service.interceptors.response.use(
(response) => {
removePending(response.config);
custom_options.loading && closeLoading(custom_options); // 关闭loading

if (
custom_options.code_message_show &&
response.data &&
response.data.code !== 0
) {
ElMessage({
type: "error",
message: response.data.message,
});
return Promise.reject(response.data); // code不等于0, 页面具体逻辑就不执行了
}

return custom_options.reduct_data_format ? response.data : response;
},
(error) => {
error.config && removePending(error.config);
custom_options.loading && closeLoading(custom_options); // 关闭loading
custom_options.error_message_show && httpErrorStatusHandle(error); // 处理错误状态码
return Promise.reject(error); // 错误继续返回给到具体页面
}
);

return service(axiosConfig);
}

export default myAxios;

/**
* 处理异常
* @param {*} error
*/
function httpErrorStatusHandle(error) {
// 处理被取消的请求
if (axios.isCancel(error))
return console.error("请求的重复请求:" + error.message);
let message = "";
if (error && error.response) {
switch (error.response.status) {
case 302:
message = "接口重定向了!";
break;
case 400:
message = "参数不正确!";
break;
case 401:
message = "您未登录,或者登录已经超时,请先登录!";
break;
case 403:
message = "您没有权限操作!";
break;
case 404:
message = `请求地址出错: ${error.response.config.url}`;
break; // 在正确域名下
case 408:
message = "请求超时!";
break;
case 409:
message = "系统已存在相同数据!";
break;
case 500:
message = "服务器内部错误!";
break;
case 501:
message = "服务未实现!";
break;
case 502:
message = "网关错误!";
break;
case 503:
message = "服务不可用!";
break;
case 504:
message = "服务暂时无法访问,请稍后再试!";
break;
case 505:
message = "HTTP版本不受支持!";
break;
default:
message = "异常问题,请联系管理员!";
break;
}
}
if (error.message.includes("timeout")) message = "网络请求超时!";
if (error.message.includes("Network"))
message = window.navigator.onLine ? "服务端异常!" : "您断网了!";

ElMessage({
type: "error",
message,
});
}

/**
* 关闭Loading层实例
* @param {*} _options
*/
function closeLoading(_options) {
if (_options.loading && LoadingInstance._count > 0) LoadingInstance._count--;
if (LoadingInstance._count === 0) {
LoadingInstance._target.close();
LoadingInstance._target = null;
}
}

/**
* 储存每个请求的唯一cancel回调, 以此为标识
* @param {*} config
*/
function addPending(config) {
const pendingKey = getPendingKey(config);
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel);
}
});
}

/**
* 删除重复的请求
* @param {*} config
*/
function removePending(config) {
const pendingKey = getPendingKey(config);
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey);
cancelToken(pendingKey);
pendingMap.delete(pendingKey);
}
}

/**
* 生成唯一的每个请求的唯一key
* @param {*} config
* @returns
*/
function getPendingKey(config) {
let { url, method, params, data } = config;
if (typeof data === "string") data = JSON.parse(data); // response里面返回的config.data是个字符串对象
return [url, method, JSON.stringify(params), JSON.stringify(data)].join("&");
}

axios 封装
https://lero.fun/2022/09/03/axios封装/
作者
Leroli
发布于
2022年9月3日
许可协议