ruoyi-vue若依前端是如何防止接口重复请求
防止接口重复请求是为了避免用户在短时间内多次点击同一个按钮,导致后端接口被多次调用,引发数据异常或性能问题。
之前的文章介绍过后端如何防止接口重复请求,这篇文章介绍一下前端如何防止接口重复提交。
按钮禁用
用户点击按钮后立即将按钮设置为禁用状态,待接口返回结果或一段时间后再恢复按钮可用。这样可以有效防止用户在请求未完成时重复点击。
const submitForm = () => {
if (!buttonDisabled) {
buttonDisabled = true;
axios.post('/api/submit', data)
.then(response => {
// 请求成功
})
.catch(error => {
// 错误处理
})
.finally(() => {
// 恢复按钮状态
buttonDisabled = false;
});
}
}
请求拦截器
以下是若依源码:
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
}
}
return config
}, error => {
Promise.reject(error)
})
这段代码是一个请求拦截器,用于拦截发送的HTTP请求,并对请求进行一些预处理操作,主要包括Token注入和防止重复提交的功能。以下是代码的具体作用:
1. Token设置
const isToken = (config.headers || {}).isToken === false;
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken();
}
- 作用:检查当前请求是否需要携带
Token
。通过config.headers.isToken
字段决定,若isToken
为false
,表示不需要携带Token,否则会将Authorization
头设置为带有Bearer
前缀的Token。 - 逻辑:
getToken()
用于获取存储的Token(比如从cookie或localStorage中),并在请求头中注入Authorization
,这样后端能够识别用户身份。
2. GET请求参数处理
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
- 作用:对于GET请求,将
params
参数序列化并拼接到URL中。 - 逻辑:如果请求是GET方法,并且存在
params
参数,那么它会将这些参数通过tansParams
方法(通常是将参数对象转为key=value
形式的字符串)拼接到URL后面。最后清空config.params
,并将新的URL赋值给config.url
。
3. 防止重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const sessionObj = cache.session.getJSON('sessionObj');
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj);
} else {
const s_url = sessionObj.url;
const s_data = sessionObj.data;
const s_time = sessionObj.time;
const interval = 1000;
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ` + message);
return Promise.reject(new Error(message));
} else {
cache.session.setJSON('sessionObj', requestObj);
}
}
}
- 作用:用于防止短时间内重复提交相同的POST或PUT请求。
- 逻辑:
- 通过检查
config.headers.repeatSubmit
字段判断是否启用防重复提交(false
表示不启用防重)。 - 对于需要防止重复提交的请求(POST或PUT方法),会将请求的
url
、data
和当前时间time
封装为requestObj
对象。 - 读取会话缓存中的上一次请求对象(
sessionObj
),如果不存在,则将当前请求对象保存到会话缓存中。 - 如果上一次请求的
url
、data
和当前请求相同,且两次请求时间间隔小于1秒钟(interval = 1000
),则视为重复提交,拦截请求并返回错误信息。 - 否则,将当前请求对象更新到缓存中,允许请求继续发送。
- 通过检查