蓝奏云直链解析工具:简化文件下载的智能解决方案
2026/1/12 7:22:33
背景问题:
需要防止重复提交请求。
方案思考:
实现请求防抖和重复提交防护。
具体实现:
请求防抖和重复提交防护:
// utils/request-guard.jsimport{ElMessage}from'element-plus'// 请求防抖和重复提交防护classRequestGuard{constructor(){// 存储待处理的请求this.pendingRequests=newMap()// 存储请求的唯一标识this.requestKeys=newMap()}// 生成请求唯一标识generateRequestKey(config){const{method,url,params,data}=configreturn`${method}-${url}-${JSON.stringify(params)}-${JSON.stringify(data)}`}// 检查请求是否正在处理中hasPendingRequest(config){constkey=this.generateRequestKey(config)returnthis.pendingRequests.has(key)}// 添加待处理请求addPendingRequest(config){constkey=this.generateRequestKey(config)this.pendingRequests.set(key,config)}// 移除待处理请求removePendingRequest(config){constkey=this.generateRequestKey(config)this.pendingRequests.delete(key)}// 取消所有待处理请求cancelAllPendingRequests(){this.pendingRequests.forEach((config,key)=>{if(config.cancelToken){config.cancelToken.cancel(`Request${key}canceled`)}})this.pendingRequests.clear()}}exportconstrequestGuard=newRequestGuard()// 防抖函数exportfunctiondebounce(func,delay){lettimeoutIdreturnfunction(...args){clearTimeout(timeoutId)timeoutId=setTimeout(()=>func.apply(this,args),delay)}}// 节流函数exportfunctionthrottle(func,limit){letinThrottlereturnfunction(){if(!inThrottle){func.apply(this,arguments)inThrottle=truesetTimeout(()=>inThrottle=false,limit)}}}更新请求工具以集成防护机制:
// utils/request.js (更新版本)importaxiosfrom'axios'import{ElMessage,ElNotification}from'element-plus'import{useUserStore}from'@/stores/modules/user'import{requestGuard}from'./request-guard'// 创建 axios 实例constservice=axios.create({baseURL:import.meta.env.VITE_APP_BASE_API||'/api',timeout:15000,headers:{'Content-Type':'application/json;charset=UTF-8'}})// 请求拦截器service.interceptors.request.use(config=>{// 防止重复请求if(requestGuard.hasPendingRequest(config)){constsource=axios.CancelToken.source()config.cancelToken=source.token source.cancel('重复请求')returnPromise.reject(newaxios.Cancel('重复请求'))}// 添加待处理请求requestGuard.addPendingRequest(config)// 添加认证 tokenconsttoken=localStorage.getItem('access_token')if(token){config.headers['Authorization']=`Bearer${token}`}// 添加请求时间戳if(config.method==='get'){config.params={...config.params,_t:Date.now()}}returnconfig},error=>{returnPromise.reject(error)})// 响应拦截器service.interceptors.response.use(response=>{// 移除待处理请求requestGuard.removePendingRequest(response.config)constres=response.dataif(res.code&&res.code!==200){ElMessage.error(res.message||'请求失败')if(res.code===401){constuserStore=useUserStore()userStore.clearUser()window.location.href='/login'}returnPromise.reject(newError(res.message||'Error'))}else{returnres}},error=>{// 移除待处理请求if(error.config){requestGuard.removePendingRequest(error.config)}if(axios.isCancel(error)){console.log('请求被取消:',error.message)returnPromise.reject(error)}letmessage='请求失败'if(error.response){conststatus=error.response.statusswitch(status){case400:message='请求参数错误'breakcase401:message='未授权,请重新登录'constuserStore=useUserStore()userStore.clearUser()window.location.href='/login'breakcase403:message='拒绝访问'breakcase404:message='请求资源不存在'breakcase500:message='服务器内部错误'breakdefault:message=`连接错误${status}`}}elseif(error.request){message='网络连接异常'}else{message=error.message}ElMessage.error(message)returnPromise.reject(error)})exportdefaultservice