axios请求封装(vue2)
import axios from 'axios';
import { ElLoading, ElMessage } from 'element-plus';
import { RefreshToken } from '@/request';
import { getlocalStorage, setlocalStorage } from '@/utils/common.js';
const pendingMap = new Map();
const LoadingInstance = {
_target: null,
_count: 0
};
const baseUrlList = {
'development': '',
'test': '', // 测试
'dev': '', // 开发
'uat': '', // uat
'production': '' // 生产
}
/**
*
* @param {请求体} axiosConfig
* @param {自定义事件配置} customOptions
* @param {loading配置} loadingOptions
* @returns
*/
function axiosPlus(axiosConfig, customOptions, loadingOptions) {
const VITE_NODE_ENV = import.meta.env.VITE_NODE_ENV;
const service = axios.create({
baseURL: baseUrlList[VITE_NODE_ENV], // 设置统一的请求前缀
timeout: 60000 // 设置统一的超时时长
});
// 自定义配置
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 获取token不要携带token
if (getlocalStorage('token')) {
config.headers.Authorization = `Bearer ${JSON.parse(getlocalStorage('token'))}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截
service.interceptors.response.use(
async response => {
removePending(response.config);
custom_options.loading && closeLoading(custom_options); // 关闭loading
// Token失效 刷新Token
if (response.data.msgCode === 'AUTH_TOKEN_EXPIRED') {
await refreshTokenAndRetry(response.config);
}
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;
},
async error => {
error.config && removePending(error.config);
custom_options.loading && closeLoading(custom_options); // 关闭loading
custom_options.error_message_show && httpErrorStatusHandle(error); // 处理错误状态码
// 如果是Token失效且未在响应拦截器中处理,尝试刷新Token并重新发送请求
if (error.response && error.response.status === 401 && !error.config.__isRetryRequest) {
await refreshTokenAndRetry(error.config);
}
return Promise.reject(error); // 错误继续返回给到具体页面
}
);
return service(axiosConfig);
}
/**
* 刷新Token并重新发送请求
* @param {*} config
*/
async function refreshTokenAndRetry(config) {
if (!pendingMap.has(getPendingKey(config))) {
// 避免重复刷新Token导致的重复请求
pendingMap.set(getPendingKey(config), axios.CancelToken.source());
try {
const { data } = await RefreshToken({ refreshToken: getlocalStorage('refreshToken') });
setlocalStorage('token', JSON.stringify(data.accessToken));
setlocalStorage('refreshToken', JSON.stringify(data.refreshToken));
// 更新token后重新发送请求
config.headers.Authorization = 'Bearer ' + data.accessToken;
config.__isRetryRequest = true; // 标记为重试请求
return axios(config);
} finally {
pendingMap.delete(getPendingKey(config));
}
}
}
export default axiosPlus;
/**
* 处理异常
* @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);
// // 如你不明白此处为什么需要传递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('&');
}

浙公网安备 33010602011771号