axios封装
最近在搭建vue3和react的后台管系统项目,两个项目中都使用axios,所以对axios进行了个比较全的封装,方便使用,下面是封装axios的思路,希望大家补足没考虑到的地方。
下载axios
css
npm install axios --save
封装axios
1. 创建axios实例
引入axios,创建axios实例,设置请求的baseURL、请求超时时间、请求头等。
javascript
import axios from 'axios';
const http = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 15000,//请求超时设置
headers: { 'Content-Type': 'application/json' }//默认请求参数格式json
});
2. 添加请求拦截器
在请求拦截器中,我们可以对请求进行一些统一的处理,例如添加请求头、添加请求参数、添加请求标识、对请求体加密等。
javascript
http.interceptors.request.use(
(config) => {
// 取消重复请求
removePendingRequest(config);
addPendingRequest(config);
// 自动添加token
const token =localStorage.getItem('token')
if (token && !config.headers.Authorization) {
config.headers.Authorization = `Bearer ${token}`;
}
// 全局loading控制(需配合状态管理)
// store.dispatch(showGlobalLoading());
return config;
},
(error) => {
return Promise.reject(error);
}
);
3. 添加响应拦截器
在响应拦截器中,我们可以对响应进行一些统一的处理,例如对token过期处理、对响应数据进行处理、对响应状态码进行统一处理、对响应错误进行统一处理等。
javascript
http.interceptors.response.use(
(response) => {
// 移除请求队列
removePendingRequest(response.config);
// 关闭全局loading
// store.dispatch(hideGlobalLoading());
// 处理业务状态码(根据后端协议调整)
const { code, data } = response.data;
if (code !== 200) {
if (code === 401) {
// token过期处理
localStorage.removeItem('token')
window.location.href = '/login';//跳到登录
}
ElMessage.error(data.message || '操作失败');
return Promise.reject(new Error(data.message ));
}
return response.data;
},
(error) => {
// 关闭loading
// store.dispatch(hideGlobalLoading());
// 处理取消的请求
if (axios.isCancel(error)) {
return Promise.reject(error);
}
// 错误处理
let errorMessage = '请求失败';
if (error.response) {
switch (error.response.status) {
//常规状态码对应错误提示
case 401:
errorMessage = '登录已过期,请重新登录';
localStorage.removeItem('token')
window.location.href = '/login';
break;
case 403:
errorMessage = '没有操作权限';
break;
case 500:
errorMessage = '服务器异常';
break;
}
} else if (error.message.includes('timeout')) {
errorMessage = '请求超时';
} else if (error.message.includes('Network Error')) {
errorMessage = '网络连接失败';
}
ElMessage.error(errorMessage);
return Promise.reject(error);
}
);
4. 创建请求队列
创建请求队列,用于存储待处理的请求,以便在请求被取消时进行相应的处理,对重复请求进行取消以提高性能,降低服务端荷载
javascript
// 请求队列
const pendingRequests = new Map();
5.添加请求到队列的方法
定义一个函数 addPendingRequest,用于添加待处理的请求,将其添加到请求队列中
arduino
// 添加请求到队列
// 定义一个函数 addPendingRequest,用于添加待处理的请求
const addPendingRequest = (config) => {
// 调用 generateReqKey 函数生成请求的唯一标识符
const requestKey = generateReqKey(config);
// 为 config 对象设置 cancelToken 属性,如果它不存在则创建一个新的 axios.CancelToken 实例
config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
// 检查 pendingRequests 中是否已经存在该请求标识符
if (!pendingRequests.has(requestKey)) {
// 如果不存在,则将请求标识符和取消函数存储到 pendingRequests 中
pendingRequests.set(requestKey, cancel);
}
});
};
6.移除请求的方法
定义一个名为 removePendingRequest 的函数,将请求从请求队列中移除,
scss
// 移除请求
// 定义一个名为 removePendingRequest 的函数,用于移除待处理的请求
const removePendingRequest = (config) => {
// 调用 generateReqKey 函数生成请求的唯一标识符
const requestKey = generateReqKey(config);
// 检查 pendingRequests 集合中是否包含当前请求的唯一标识符
if (pendingRequests.has(requestKey)) {
// 如果存在,则获取对应的取消请求的函数
const cancel = pendingRequests.get(requestKey);
// 调用取消请求的函数,并传入请求的唯一标识符
cancel(requestKey);
// 从 pendingRequests 集合中删除该请求的唯一标识符
pendingRequests.delete(requestKey);
}
};
7.生成请求标识
生成请求的唯一标识符,用于在请求队列中查找和删除重复请求。
arduino
// 生成请求标识
// 定义一个名为 generateReqKey 的函数,用于生成请求的唯一标识符
const generateReqKey = (config) => {
// 返回一个由多个部分组成的字符串,这些部分通过 '&' 符号连接
return [
// config.method 表示请求的方法,如 GET、POST 等
config.method,
// config.url 表示请求的 URL 地址
config.url,
// JSON.stringify(config.params) 将请求的参数对象转换为 JSON 字符串
JSON.stringify(config.params),
// JSON.stringify(config.data) 将请求的数据对象转换为 JSON 字符串
JSON.stringify(config.data)
].join('&');
};
8.根据restful规范封装常用方法并导出
根据restful规范封装常用方法,例如get、post、put、patch、delete,upload等方法,并导出
javascript
// 封装常用方法(支持自动重试)
export const request = {
get: (url, params, config) => http.get(url, { params, ...config }),
post: (url, data, config) => http.post(url, data, config),
put: (url, data, config) => http.put(url, data, config),
patch: (url, data, config) => http.patch(url, data, config),
delete: (url, params, config) => http.delete(url, { params, ...config }),
upload: (url, file, config) => {
const formData = new FormData();
formData.append('file', file);
return http.post(url, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
...config
});
},
// 带自动重试的请求
retry: async (fn, retries = 3, delay = 1000) => {
try {
return await fn();
} catch (error) {
return retries > 1
? new Promise(resolve =>
setTimeout(() => resolve(request.retry(fn, retries - 1, delay)), delay)
)
: Promise.reject(error);
}
}
};
完整代码
javascript
import axios from 'axios';
import { ElMessage } from 'element-plus' // 使用element-plus消息提示
// 创建axios实例
const http = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 15000,//请求超时设置
headers: { 'Content-Type': 'application/json' }
});
// 请求队列
const pendingRequests = new Map();
// 生成请求标识
// 定义一个名为 generateReqKey 的函数,用于生成请求的唯一标识符
const generateReqKey = (config) => {
// 返回一个由多个部分组成的字符串,这些部分通过 '&' 符号连接
return [
// config.method 表示请求的方法,如 GET、POST 等
config.method,
// config.url 表示请求的 URL 地址
config.url,
// JSON.stringify(config.params) 将请求的参数对象转换为 JSON 字符串
JSON.stringify(config.params),
// JSON.stringify(config.data) 将请求的数据对象转换为 JSON 字符串
JSON.stringify(config.data)
].join('&');
};
// 添加请求到队列
// 定义一个函数 addPendingRequest,用于添加待处理的请求
const addPendingRequest = (config) => {
// 调用 generateReqKey 函数生成请求的唯一标识符
const requestKey = generateReqKey(config);
// 为 config 对象设置 cancelToken 属性,如果它不存在则创建一个新的 axios.CancelToken 实例
config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
// 检查 pendingRequests 中是否已经存在该请求标识符
if (!pendingRequests.has(requestKey)) {
// 如果不存在,则将请求标识符和取消函数存储到 pendingRequests 中
pendingRequests.set(requestKey, cancel);
}
});
};
// 移除请求
// 定义一个名为 removePendingRequest 的函数,用于移除待处理的请求
const removePendingRequest = (config) => {
// 调用 generateReqKey 函数生成请求的唯一标识符
const requestKey = generateReqKey(config);
// 检查 pendingRequests 集合中是否包含当前请求的唯一标识符
if (pendingRequests.has(requestKey)) {
// 如果存在,则获取对应的取消请求的函数
const cancel = pendingRequests.get(requestKey);
// 调用取消请求的函数,并传入请求的唯一标识符
cancel(requestKey);
// 从 pendingRequests 集合中删除该请求的唯一标识符
pendingRequests.delete(requestKey);
}
};
// 请求拦截器
http.interceptors.request.use(
(config) => {
// 取消重复请求
removePendingRequest(config);
addPendingRequest(config);
// 自动添加token
const token =localStorage.getItem('token')
if (token && !config.headers.Authorization) {
config.headers.Authorization = `Bearer ${token}`;
}
// 全局loading控制(需配合状态管理)
// store.dispatch(showGlobalLoading());
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
http.interceptors.response.use(
(response) => {
// 移除请求队列
removePendingRequest(response.config);
// 关闭loading
// store.dispatch(hideGlobalLoading());
// 处理业务状态码(根据后端协议调整)
const { code, data } = response.data;
if (code !== 200) {
if (code === 401) {
// token过期处理
localStorage.removeItem('token')
window.location.href = '/login';
}
ElMessage.error(data.message || '操作失败');
return Promise.reject(new Error(data.message ));
}
return response.data;
},
(error) => {
// 关闭loading
// store.dispatch(hideGlobalLoading());
// 处理取消的请求
if (axios.isCancel(error)) {
console.log('请求被取消:', error.message);
return Promise.reject(error);
}
// 错误处理
let errorMessage = '请求失败';
if (error.response) {
switch (error.response.status) {
case 401:
errorMessage = '登录已过期,请重新登录';
localStorage.removeItem('token')
window.location.href = '/login';
break;
case 403:
errorMessage = '没有操作权限';
break;
case 500:
errorMessage = '服务器异常';
break;
}
} else if (error.message.includes('timeout')) {
errorMessage = '请求超时';
} else if (error.message.includes('Network Error')) {
errorMessage = '网络连接失败';
}
ElMessage.error(errorMessage);
return Promise.reject(error);
}
);
// 封装常用方法(支持自动重试)
export const request = {
get: (url, params, config) => http.get(url, { params, ...config }),
post: (url, data, config) => http.post(url, data, config),
put: (url, data, config) => http.put(url, data, config),
patch: (url, data, config) => http.patch(url, data, config),
delete: (url, params, config) => http.delete(url, { params, ...config }),
upload: (url, file, config) => {
const formData = new FormData();
formData.append('file', file);
return http.post(url, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
...config
});
},
// 带自动重试的请求
retry: async (fn, retries = 3, delay = 1000) => {
try {
return await fn();
} catch (error) {
return retries > 1
? new Promise(resolve =>
setTimeout(() => resolve(request.retry(fn, retries - 1, delay)), delay)
)
: Promise.reject(error);
}
}
};
// 自定义Hook方式封装
// 导出一个名为 useHttp 的常量,它是一个函数
export const useHttp = () => {
// 可以在此集成全局loading状态管理
return {
...request,
// 扩展其他方法...
};
};
总结:封装一个适用于大部分后台管理系统的axios,导出了get,post,put,patch,delete,puload等result规范常用的请求方式,自动重试的请求方法。处理了常见的状态码,取消连续发送的相同请求,提高性能,减少服务器负载。