前言
大家在编写管理后端这种项目如何处理表单的重复请求的呢,我估计绝大部分是用loading处理的吧,用户点击的之后让按钮loading,直到请求完成之后再恢复;当然这样是可以的,只不过如果当页面比较多,这种方式实现起来相对来说比较麻烦;那么今天就说一个相对来说比较简单的方法
axios
项目是基于axios实现的功能;
主要实现原理,再请求拦截器中判断请求的url和请求方式还是请求参数是否一致,如果一致,代表是同一个请求,那么后面的请求不执行,直到第一个请求完成
代码:
import axios from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import 'element-plus/theme-chalk/el-message.css';
import router from '../router/index';
// const noToken = ['/login', '/register', '/like/count'];
const token = ['/like', '/comment'];
const request = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
timeout: 5000,
headers: {
Authorization:
JSON.parse(localStorage.getItem('userInfo') || '{}')['token-web'] || '',
noToken: true,
},
});
const newSet = new Set();
// 将请求地址和请求参数和方法生成一个字符串
const genKey = (config: any) => {
const { method, url, data } = config;
return data
? [
url,
method,
typeof data !== 'string'
? JSON.stringify(data)
: JSON.stringify(JSON.parse(data)),
].join('&')
: [url, method].join('&');
};
// 添加请求拦截器
request.interceptors.request.use(
(config: any) => {
// console.log(router)
// router.push('/login')
// 如果请求地址包含like,删除请求头中的noToken
if (token.some((path) => config.url.indexOf(path) !== -1)) {
delete config.headers.noToken; // 如果 URL 包含数组中的任意一个元素,则删除 noToken
}
if (config.url.indexOf('/like/count') !== -1) {
config.headers.noToken = true;
}
// 在发送请求之前做些什么 token
// 获取请求地址和请求参数
const key = genKey(config);
// 每次请求前读取newSet中有没有key
if (newSet.has(key)) {
// 如果有,说明重复请求,返回一个错误
return Promise.reject('重复请求');
}
// 如果没有,将key添加到newSet中
newSet.add(key);
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
config.headers.Authorization = 'Bear ' + userInfo['token-web'] || '';
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
},
);
// 添加响应拦截器
request.interceptors.response.use(
(response) => {
// 对响应数据做点什么
const key = genKey(response.config);
// 每次请求后删除newSet中的key
newSet.delete(key);
console.log('添加响应拦截器');
const res = response.data;
// console.log(response)
if (res.code && res.code !== 0) {
// `token` 过期或者账号已在别处登录
if (res.code === 401 || res.code === 4001) {
router.push('/login');
ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
.then(() => {})
.catch(() => {});
}
return res;
} else {
return res;
}
},
(error) => {
if (error === '重复请求') {
return Promise.reject('重复请求');
}
console.log(error);
const key = genKey(error.config);
// 每次请求后删除newSet中的key
newSet.delete(key);
console.log('添加响应拦截器');
if (error.message === 'canceled') {
return Promise.reject('取消请求');
}
if (error.response.data && error.response.data.message) {
ElMessage.error(error.response.data.message);
return error.response.data;
} else {
if (error.response.status === 401) {
localStorage.clear();
return;
}
// 对响应错误做点什么
if (error.message.indexOf('timeout') != -1) {
ElMessage.error('网络超时');
} else if (error.message == 'Network Error') {
ElMessage.error('网络连接错误');
} else {
if (error.response.data) ElMessage.error(error.response.statusText);
else ElMessage.error('接口路径找不到');
// ElMessage.error(error.message)
}
return Promise.reject(error);
}
},
);
// 导出 axios 实例
export default request;
- 当发送请求的时候先判断newSet中是否已经包含了当前发送的请求,如果已经包含,则表示发送的是重复请求,如果没有,正常发送,且将这个请求放入newSet中
- 当请求发送完成之后,不论成功还是失败,都将这个请求从newSet中移出,防止下次请求被拦截
查看实际效果
vue页面
<template>
<div>
<el-button @click="requestAxios">发送请求</el-button>
</div>
</template>
<script setup lang="ts">
import request from '@/request/index';
import { test2 } from '@/request/test/index';
const requestAxios = () => {
request.post('/api/table', {
page: 1,
limit: 10,
size: {
id: 1,
},
});
request.post('/api/table', {
page: 1,
limit: 10,
size: {
id: 1,
},
});
request.get('/api/table?page=1&limit=10');
request.get('/api/table/1');
};
</script>
<style lang="scss" scoped></style>
正常不做处理的话,点击发送请求,会发送四个请求,如果做处理之后结果
只发送了三个请求,说明成功,再次点击发送,发现还是这三个请求,说明请求完成之后数据从newSet中移出了;
当短时间重复发送请求
全部被拦截了,达到了我们一开始说的那种功能
总结
以上就是实现的一种简单方法,当然源代码中的处理url、请求方式和参数的方式并不太好,大家可以自定义处理一下,如有问题请留言
npm i vue-template-cli-sys
用vue3+ts+element-plus+pinia编写的一个后台管理模板,里面的axios就是这种方式,如果大家不想重新搭建一个管理后台模板,即可安装,使用方式vsystemplate create 项目名称