防止重复提交请求

前景提要:
ts 简易封装 axios,统一 API
实现在 config 中配置开关拦截器
axios 实现请求 loading 效果

用一个数组保存当前请求的 url,此时还未响应。如果再次发起同样请求,比对 url 发现已经存在数组中,则拦截请求,提示重复提交。当该请求响应结束后,就将 url 从数组中剔除。则可再次发起上一次 url 的请求。

封装拦截器

typescript 复制代码
import { AxiosError } from "axios";
import { ElMessage } from "element-plus";
import { MyAxiosResponse, MyInternalAxiosRequestConfig } from "./request";

let pendingUrl: string[] = []; // pending 状态的请求 url
const excludeUrl: string[] = ["/upload", "/upload/chunk"]; // 排除不需要拦截的url。比如并发上传文件的 url 都是一样的,就需要排除

/**
 * 比较请求的 url
 * @param {import("..").AxiosRequestConfig} config
 * @returns
 */
export function compareUrl(config: MyInternalAxiosRequestConfig) {
    // 配置中明确标出 debounce 为 false,则关闭请求防抖。不写或者为 true,则开启防抖
    if (config.debounce === false) return config;

    // 白名单 url 不防抖处理
    if (config.url && excludeUrl.includes(config.url)) return config;

    if (config.url && pendingUrl.includes(config.url)) {
        // alert("重复请求");
        ElMessage.warning("请求频繁");
        throw new Error("请求频繁");
    } else {
        config.url && pendingUrl.push(config.url);
    }
    return config;
}

/**
 * 请求成功,过滤请求完毕的 url
 * @param {import("axios").AxiosResponse} res
 * @returns
 */
export function filterFulfilledUrlOnFulfilled(res: MyAxiosResponse) {
    pendingUrl = pendingUrl.filter(item => item != res.config.url);
    return res;
}

/**
 * 请求错误,过滤请求完毕的 url
 * @param {import("axios").AxiosError} err
 * @returns
 */
export function filterFulfilledUrlOnRejected(err: AxiosError) {
    pendingUrl = pendingUrl.filter(item => item != err?.config?.url);
    throw err;
}

补充 config 配置并注册拦截器

typescript 复制代码
const DEFAULT_EXTRA_FEATURE_CONFIG = { showLoading: true, showMessage: true, debounce: true, retry: true };

/** 扩展 axios 的请求配置类型 */
export interface MyAxiosRequestConfig<TReqBodyData = any> extends AxiosRequestConfig<TReqBodyData> {
    interceptors?: {
        reqInterceptorOnFulfilled?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig;
        reqInterceptorOnRejected?: (err: AxiosError) => any;
        resInterceptorOnFulfilled?: (res: AxiosResponse) => AxiosResponse;
        resInterceptorOnRejected?: (err: AxiosError) => Promise<AxiosError>;
    };
    showLoading?: boolean;
    showMessage?: boolean;
    debounce?: boolean;
    retry?: boolean;
}

/** 给拦截器使用 */
export interface MyInternalAxiosRequestConfig extends InternalAxiosRequestConfig {
    showLoading?: boolean;
    showMessage?: boolean;
    debounce?: boolean;
    retry?: boolean;
}
typescript 复制代码
import HttpRequest from "./http/request";
import { compareUrl, filterFulfilledUrlOnFulfilled, filterFulfilledUrlOnRejected } from "./http/debounceReq";
import { closeLoadingOnFulfilled, closeLoadingOnRejected, showLoading } from "./http/loading";
import { responseMessageOnFulfilled } from "./http/message";
import { getTokenResponseInterceptor, setAccessTokenRequestInterceptor } from "./http/token";
import { retryRequest } from "./http/retryRequest";

const httpRequest = new HttpRequest({
    baseURL: import.meta.env.VITE_APP_API_BASE_URL,
    timeout: import.meta.env.VITE_APP_API_TIMEOUT
});

// loading
httpRequest.getInstance().interceptors.request.use(showLoading);
httpRequest.getInstance().interceptors.response.use(closeLoadingOnFulfilled, closeLoadingOnRejected);

// debounceRequest
httpRequest.getInstance().interceptors.request.use(compareUrl);
httpRequest.getInstance().interceptors.response.use(filterFulfilledUrlOnFulfilled, filterFulfilledUrlOnRejected);

export default httpRequest;
``
相关推荐
尾善爱看海9 小时前
不常用的浏览器 API —— Web Speech
前端
美酒没故事°10 小时前
vue3拖拽+粘贴的综合上传器
前端·javascript·typescript
jingling55511 小时前
css进阶 | 实现罐子中的水流搅拌效果
前端·css
南村群童欺我老无力.12 小时前
Flutter 框架跨平台鸿蒙开发 - 打造安全可靠的密码生成器,支持强度检测与历史记录
flutter·华为·typescript·harmonyos
悟能不能悟12 小时前
前端上载文件时,上载多个文件,但是一个一个调用接口,怎么实现
前端
可问春风_ren13 小时前
前端文件上传详细解析
前端·ecmascript·reactjs·js
羊小猪~~14 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
晚风资源组15 小时前
CSS文字和图片在容器内垂直居中的简单方法
前端·css·css3
Miketutu15 小时前
Flutter学习 - 组件通信与网络请求Dio
开发语言·前端·javascript
摘星编程15 小时前
React Native for OpenHarmony 实战:Swiper 滑动组件详解
javascript·react native·react.js