axios過濾重複請求及批量取消請求

實現思路:

把每次發送的請求及參數通過CancelToken創建標記添加到vuex的一個列表中,響應完成後從vuex的列表中刪除該標記
每次添加標記到列表時先與列表比對是否重複,未重複正常添加,重複則不添加並取消該次請求。
通過循環vuex的請求標記列表,可以批量取消所有請求。

http.ts

該文件封裝了axios的請求,對每次的請求通過cancel的createInterceptUrl方法創建標識,然後通過axiosPendingModule的addPending方法記錄在vuex的列表中,響應結束後通過cancel的delayRepeatRequest從vuex中移除已經完成的請求

import axios from 'axios';
import qs from 'qs';
import { axiosPendingModule } from '@/store/modules/pending'; //請求連接狀態列隊
import { CancelToken, createInterceptUrl, removeRepeatRequest, delayRepeatRequest } from './cancel';

//ts接口
interface IParams {
    [key: string]: any;
}

interface IHttp {
    [key: string]: Function;
}

interface IStatusMessage {
    [key: number]: string;
}


/* 通用設置 */
axios.defaults.timeout = 1000 * 30; //請求超時設置,30秒超時
axios.defaults.baseURL = 'http://localhost:8080';

/* 請求攔截 */
axios.interceptors.request.use(
    (config: any) => {
        //在一個axios發送前執行一下取消重複請求操作
        removeRepeatRequest(config);

        /* 創建取消請求的token,並把本次請求的信息添加到請求列表 */
        config.cancelToken = new CancelToken((c) => {
            axiosPendingModule.addPending({
                u: createInterceptUrl(config),
                f: c,
            });
        });

        return config;
    },
    (error: any) => {
        return Promise.reject(error);
    }
);

//錯誤信息
let errorMessage: any = null;

/* 響應攔截 */
axios.interceptors.response.use(
    (response) => {
        //在一個axios響應後再執行一下取消操作,把已經完成的請求從pendingList中移除
        delayRepeatRequest(response.config);

        //狀態碼爲200表示接口請求成功
        if (response.status === 200) {
            
            //code爲0表示結果正常
            if (response.data && response.data.code === 0) {
                return Promise.resolve(response);
            } else {
                //異常狀態碼提示信息
                errorMessage && errorMessage.close();
                errorMessage = Message({
                    message: response.data.msg || 'Error',
                    type: 'error',
                    duration: 5 * 1000,
                });

                return Promise.reject(response);
            }
        } else {
            return Promise.reject(response);
        }
    },
    (error: any) => {
        if (error.response && error.response.status) {
        	let statusMessage: IStatusMessage = {
			    400: '錯誤請求',
			    401: '未授權,請重新登錄',
			    403: '拒絕訪問',
			    404: '請求錯誤,未找到該資源',
			    405: '請求方法未允許',
			    408: '請求超時',
			    500: '服務器端出錯',
			    502: '網絡錯誤',
			    503: '服務不可用',
			    504: '網絡超時',
			    505: 'http版本不支持該請求',
			};
            error.message = statusMessage[error.response.status] || `連接錯誤${error.response.status}`;
        }
        return Promise.reject(error);
    }
);

/**
 * 公共請求
 * @param {String} type 請求類型
 * @param {String} url 請求的url地址
 * @param {Object} params 請求提交的參數
 */
function commonRequest(type: string, url: string, params: any = {}, config: any = { headers: { 'Content-Type': 'application/json' } }) {
    return new Promise((resolve, reject) => {
        let axiosParams: Array<any> = []; //axios方法請求的參數

        if (UserModule.token) {
            config.headers['Authorization'] = 'Bearer' + UserModule.token;
        }

        // get、delete共用請求
        if (type === 'get' || type === 'delete') {
            config[type === 'get' ? 'params' : 'data'] = params;
            axiosParams = [url, config];
        } else {
            // post、put、patch共用請求

            //不需要序列化參數的情況
            let notStringify = config.headers['Content-Type'] === 'application/json' || config.headers['Content-Type'] === 'multipart/form-data';

            //需要則qs序列化參數
            if (!notStringify) {
                params = qs.stringify(params, { arrayFormat: 'indices', allowDots: true });
            }
            axiosParams = [url, params, config];
        }
		
		
        (<IParams>axios)[type](...axiosParams).then(
            (res: any) => {
                resolve(res && res.data);
            },
            (err: any) => {
                reject(err);
            }
        );
    });
}

let http: IHttp = {};
let types: Array<string> = ['get', 'post', 'put', 'delete', 'patch'];

types.forEach((type: string) => {
    http[type] = (url: string, params: any, config: any) => {
        return commonRequest(type, url, params, config);
    };
});

export default http;

pending.ts

該文件用於對記錄在vuex的請求列表的增、刪以及執行取消請求動作

import { VuexModule, Module, Mutation, Action, getModule } from 'vuex-module-decorators';
import store from '@/store';

@Module({ name: 'axiosPending', dynamic: true, namespaced: true, store })
export default class AxiosPending extends VuexModule {
    public pendingList: any[] = [];

    get getPendingList() {
        return this.pendingList;
    }

    /* 添加請求到列表 */
    @Mutation
    private ADD_PENDING(pending: any) {
        this.pendingList.push(pending);
    }

    /* 從列表刪除取消請求 */
    @Mutation
    private REMOVE_PENDING(pending: any) {
        debugger;
        let index = this.pendingList.indexOf(pending);
        index > -1 && this.pendingList.splice(index, 1);
        console.log(index);
        pending.f('取消請求'); //執行取消操作
    }

    /* 添加請求到列表 */
    @Action
    public addPending(request: any) {
        this.ADD_PENDING(request);
    }

    /* 從列表刪除取消請求 */
    @Action
    public removePending(pending: any) {
        this.REMOVE_PENDING(pending);
    }

    /* 取消所有請求 */
    @Action
    public cancelAllPending() {
        for (let i = this.pendingList.length - 1; i >= 0; i--) {
            this.REMOVE_PENDING(this.pendingList[i]);
        }
    }
}

export const axiosPendingModule = getModule(AxiosPending);

cancel.ts

該文件封裝了創建請求標識、移除重複請求、延期請求等一些常用的操作方法

import axios from 'axios';
import qs from 'qs';
import { axiosPendingModule } from '@/store/modules/pending';

/* 取消請求設置 */
export const CancelToken = axios.CancelToken;

/**
 * 創建用於攔截請求的標識,爲保證請求信息完整,組合了請求的地址、類型、參數
 * @param {Object} config  axios請求的配置對象
 * @returns {String}  返回用於攔截的字符串標識
 */
export function createInterceptUrl(config: any) {
    //處理FormData不能直接被序列化的問題
    let dataStringify = '';
    //formdata特殊處理
    if (config.data instanceof FormData) {
        let file = config.data.get('file');
        dataStringify = qs.stringify(file);
    } else {
        dataStringify = qs.stringify(config.data);
    }
    let url = config.url + '&' + config.method + '&' + qs.stringify(config.params) + '&' + dataStringify;

    return url;
}

/**
 * 移除重複請求
 * @param {Object} config  請求配置對象
 */
export function removeRepeatRequest(config: any) {
    let currentPending = createInterceptUrl(config); //本次請求信息

    let pendingList = axiosPendingModule.getPendingList;
    pendingList.some((pending) => {
        //歷史請求信息(請求的config.url中不包含baseUrl,但響應中的url會包含baseUrl,此處先處理兩者差異)
        let historyPending = config.url.includes(axios.defaults.baseURL) ? axios.defaults.baseURL + pending.u : pending.u;

        if (currentPending === historyPending) {
            axiosPendingModule.removePending(pending);
        }
    });
}

/**
 * 延遲請求,相同請求不得在短時間內重複發送
 * @param {Object} config - 請求配置
 */
export function delayRepeatRequest(config: any) {
    setTimeout(() => {
        removeRepeatRequest(config);
    }, 500);
}

export default {
    CancelToken,
    createInterceptUrl,
    removeRepeatRequest,
    delayRepeatRequest,
};

取消所有請求應用示例

import { axiosPendingModule } from '@/store/modules/pending';

axiosPendingModule.cancelAllPending(); //取消所有請求
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章