axios中的请求拦截(防止屡次点击按钮屡次请求形成的带宽浪费)

axios中的请求拦截(防止屡次点击按钮屡次请求形成的带宽浪费)

原理:axios请求中有一个cancel token的api

在网上找了许多请求的拦截,但不少都是在请求开始时进行数组存储CancelToken方法与比对信息,返回后进行处理。按照其代码实现了一遍,结果发现双击两次能够实现请求cancel,但屡次点击会发生屡次请求的状况,故思索了一遍缘由。javascript

只有第一次会被释放

如下是未修改前的代码(一些注释是我本身的理解,错误的话还望指出):

// req-intercept.js

let requestList = []; // api请求记录

/** * 将当前请求记录到缓存中 * @param {any} config 请求的内容 * @param {function} funcCancel 取消请求函数 */
export const add = (config, funcCancel) => {
    if (!config) {
        return false;
    }

    let req = genReq(config);
    for (let index = 0, len = requestList.length; index < len; index++) {
        if (req === requestList[index].req) {
            requestList[index].funcCancel();
            // requestList.splice(index, 1); // 把这条记录从数组中移除
            // console.log('cancel request:', config.url);
            return;
        }
    }

    requestList.push({
        req: genReq(config),
        funcCancel
    });
};

/** * 将请求完成的对象从缓存中移除 * @param {any} config 请求对象 */
export const remove = (config) => {
    if (!config) {
        return false;
    }
    let req = genReq(config);
    for (let index = 0, len = requestList.length; index < len; index++) {
        if (req === requestList[index].req) {
            requestList.splice(index, 1); // 把这条记录从数组中移除
            break;
        }
    }
};

// 当前请求的api是否已有记录
export const has = (config) => {
    if (!config) {
        return false;
    }
    let req = genReq(config);
    for (let index = 0, len = requestList.length; index < len; index++) {
        if (req === requestList[index].req) {
            return true;
        }
    }
    return false;
};

/** * 生成请求记录对象,方面对比 * @param {object} config 请求对象 */
const genReq = (config) => {
    if (!config) {
        return '';
    }
    let arrayReq = [];
    arrayReq.push(`url:${config.url},`);
    arrayReq.push(`method:${config.method},`);
    arrayReq.push(`params:${json2Form(config.params)},`);
    arrayReq.push(`header:${json2Form(config.header)}`);
    // let key = 'Method: ' + config.method + ',Url: ' + url + ',Data: ' + json2Form(data) + ', header:' + json2Form(header);
    return arrayReq.join('');
};

const json2Form = (json) => {
    var str = [];
    for (var key in json) {
        if (key !== 't') {
            str.push(encodeURIComponent(key) + '=' + encodeURIComponent(json[key]));
        }
    }
    return str.join('&');
};
复制代码
// index.js

import axios from 'axios';
import JSONbig from 'json-bigint';
import * as reqIntercept from './req-intercept.js';
let cancelToken = axios.CancelToken;
let gOpts = {};
const JSONbigString = JSONbig({'storeAsString': true});

let axiosNew = axios.create({
    transformResponse: [function (data) {
        // return JSONbig.parse(data)
        // return JSON.parse(data);

        // 处理数据中的精度溢出的数字,将溢出的数字转换成字符串
        return JSONbigString.parse(data);
    }]
});

function defaultHeader () {
    return {};
};
/** * 分析后端返回的错误信息 * @param responseHeader 后台的相应头对象 */
const analyzeException = (responseHeader) => {
    if (responseHeader) {
        const errCode = responseHeader['head.err.code'];
        const errMsg = responseHeader['head.err.msg'];

        return {
            errCode,
            // errMsg: errMsg ? decodeURIComponent(errMsg) : SERVICE_ERROR_STATUS[errCode]
            errMsg: errMsg ? decodeURIComponent(errMsg) : errCode
        };
    }
};

// 请求拦截器,每一个请求发出以前须要经过此函数处理一遍
axiosNew.interceptors.request.use(function (config) {
    config.cancelToken = new cancelToken((c) => {
        reqIntercept.add(config, c);
    });
    config.baseURL = gOpts.baseURL;
    // 注入自定义请求头
    config.headers = Object.assign({}, config.headers, defaultHeader());
    return config;
}, function (error) {
    return Promise.reject(error);
});

// 响应拦截器,从服务端获取的数据,都统一处理
axiosNew.interceptors.response.use(function (response) {
    reqIntercept.remove(response.config); // 在一个ajax响应后再执行一下取消操做,把已经完成的请求从pending中移除
    if (response.headers) {
        if (response.status === 200) {
            return response.data;
        } else {
            return Promise.reject(analyzeException(response.headers));
        }
    } else {
        // 兼容webpack-dev-server热加载,拦截器中获取的response只有消息体,直接返回消息体的内容
        return response;
    }
}, function (error) {
    if (!error.__CANCEL__) {
        return Promise.reject(analyzeException(error.response));
    }
    // return Promise.reject(analyzeException(error.response.headers));
});

const request = (url, params, method = 'GET', headerData = {}) => {
    method = method.toUpperCase();
    let config = {
        url: url,
        method: method,
        headers: headerData
    };
    if (method === 'POST' || method === 'PUT') {
        Object.assign(config, {
            data: params
        });
    } else {
        Object.assign(config, {
            params: Object.assign({}, params, {
                t: new Date().getTime()
            })
        });
    }
    return new Promise((resolve, reject) => {
        axiosNew(config).then((data) => {
            if (data) {
                resolve(data);
            } else {
                resolve(undefined);
            }
        }).catch(error => {
            reject(new Error(error));
        });
    });
};

export const axiosInterceptors = (target) => {
    target.prototype.responseInterceptors = (response) => {
        console.log('responseInterceptors11');
        return response;
    };

    target.prototype.fetch = (url, params, method, headerData = {}) => {
        return request(url, params, method, headerData);
    };
    /** 设置全局配置参数 */
    target.prototype.setOpts = (opts) => {
        gOpts = opts;
    };
};

复制代码

随后查找缘由,发如今req-intercept.js中的add方法中,若是请求的req判断一致后,会执行cancelToken函数,但随后也会被终止push;java

/** * 将当前请求记录到缓存中 * @param {any} config 请求的内容 * @param {function} funcCancel 取消请求函数 */
export const add = (config, funcCancel) => {
    if (!config) {
        return false;
    }

    let req = genReq(config);
    for (let index = 0, len = requestList.length; index < len; index++) {
        if (req === requestList[index].req) {
            requestList[index].funcCancel();
            // requestList.splice(index, 1); // 把这条记录从数组中移除
            // console.log('cancel request:', config.url);
            return;
        }
    }

    requestList.push({
        req: genReq(config),
        funcCancel
    });
};
复制代码

随后去除了return以后:

去除return

这样的请求在我看来是不正确的,很是使人不适。webpack

随后的修改结果:

/** * 将当前请求进行去重 * @param {object} req 当前请求 * @param {array} requestList api请求记录 * @param {string} key 比对的信息key值 * @param {number} num 比对的个数 */
class repetition {
    constructor ({
        req,
        requestList,
        key,
        num
    }) {
        this.requestList = requestList;
        this.req = req;
        this.requestList.push(req);
        this.key = key;
        this.num = num || 1;
        this.cancelList = [];
    }
    cancelReq () {
        let count = 0;
        for (let i = 0; i < this.requestList.length; i++) {
            console.log(this.req[this.key], this.requestList[i][this.key]);
            if (this.req[this.key] === this.requestList[i][this.key]) {
                if (count > 0) {
                    this.requestList[i].funcCancel();
                    console.log('请求被释放');
                    this.cancelList.push(i);
                }
                count++;
            }
        }

        for (let j = 0; j < this.cancelList.length; j++) {
            this.requestList.splice(this.cancelList[j], 1);
        }
        this.cancelList = [];
        return count;
    }
}

/** * 将当前请求记录到缓存中 * @param {any} config 请求的内容 * @param {function} funcCancel 取消请求函数 */
export const add = (config, funcCancel) => {
    if (!config) {
        return false;
    }

    let obj = {
        req: genReq(config),
        funcCancel
    };
    let repetit = new repetition({
        req: obj,
        requestList,
        key: 'req'
    });
    repetit.cancelReq();
    // for (let index = 0, len = requestList.length; index < len; index++) {
    // if (req === requestList[index].req) {
    // console.error('-------执行funcCancel---------');
    // requestList[index].funcCancel();
    // // requestList.splice(index, 1); // 把这条记录从数组中移除
    // // console.log('cancel request:', config.url);
    // return;
    // }
    // }
};
复制代码

实验的结果:ios

相同的请求

如今达到的效果是:相同的接口请求,只有等上个结果请求返回后才能进行下个请求。web

如下是所有代码:ajax

// index.js
import axios from 'axios';
import JSONbig from 'json-bigint';
import { Message } from 'element-ui';
import { getGlobalConf } from '@/utils/grocer';
// import { APP_ID, SERVICE_ERROR_STATUS } from '@/common/constants';
import * as reqIntercept from './req-intercept.js';

let gOpts = {};
let cancelToken = axios.CancelToken;
const conf = getGlobalConf();
const JSONbigString = JSONbig({'storeAsString': true});

let axiosNew = axios.create({
    transformResponse: [function (data) {
        // return JSONbig.parse(data)
        // return JSON.parse(data);
        // 处理数据中的精度溢出的数字,将溢出的数字转换成字符串
        return JSONbigString.parse(data);
    }]
});

function defaultHeader () {
    return {
        // 'apiVersion': '1.0'
        // 'zhsession': getZHSessionStore()
    };
};

/** * 检查登陆状态,若是登陆超时,跳转到登陆页面 * @returns 登陆超时返回 false */
const checkLogin = (resData) => {
    if (resData && typeof resData === 'string' && resData.indexOf('忘记密码1') > -1) {
        window.location.href = '/base/login';
        return false;
    }
    return true;
};

/** * 分析后端返回的错误信息 * @param responseHeader 后台的相应头对象 */
const analyzeException = (responseData) => {
    if (responseData) {
        return {
            resultCode: responseData.code,
            // errMsg: errMsg ? decodeURIComponent(errMsg) : SERVICE_ERROR_STATUS[errCode]
            resultMsg: responseData.code
        };
    }
};

// 请求拦截器,每一个请求发出以前须要经过此函数处理一遍
axiosNew.interceptors.request.use(function (config) {
    if (config.url.indexOf('http://') < 0) {
        config.baseURL = conf.baseURL;
    }
    config.cancelToken = new cancelToken((c) => {
        console.log('已添加');
        reqIntercept.add(config, c);
    });
    console.log(3232);
    config.baseURL = gOpts.baseURL;
    // 注入自定义请求头
    config.headers = Object.assign({}, config.headers, defaultHeader());
    return config;
}, function (error) {
    return Promise.reject(error);
});

// 响应拦截器,从服务端获取的数据,都统一处理
axiosNew.interceptors.response.use(function (response) {
    reqIntercept.remove(response.config); // 在一个ajax响应后再执行一下取消操做,把已经完成的请求从pending中移除
    console.log(response);
    if (response.status === 200) {
        if (!checkLogin(response.data)) {
            return;
        }
        if (response.config.method.toUpperCase() !== 'GET') {
            console.log(response, 'get');
            // 兼容企业库和圈子的数据接口
            if (response.data && (response.data.code === 200 || response.data.code === 0 || response.data.errorCode === 0 || response.data.errcode === 0 || response.data.errCode === 0 || response.data.statusCode === '200')) {
                Message({
                    title: '成功',
                    message: '操做完成',
                    type: 'success'
                });
            } else {
                if (response.data.statusCode) {
                    return response;
                } else {
                    let errMsg = response.data.msg || response.data.errmsg || '';
                    Message({
                        title: '失败',
                        message: errMsg,
                        type: 'error'
                    });
                    throw new Error(errMsg);
                }
            }
        }

        if (response.config.method.toUpperCase() === 'POST' && response.data.statusCode) {
            console.log(response, 'post');
            return response;
        }
        console.log(response, 'no');
        return response.data;
    } else {
        Message({
            title: '失败',
            message: '系统异常,请稍后重试。。',
            type: 'error'
        });
        // return Promise.reject(analyzeException(response));
        throw new Error('系统异常,请稍后重试。。');
        // return response.data;
    }
}, function (error) {
    // 判断是否为登陆超时。若是登陆超时,跳转到登陆页面
    if ('response' in error && error.response === undefined) {
        window.location.href = '/base/login';
        return;
    }
    if ('message' in error) {
        return;
    }
    // console.log('axiosNew.interceptors.response error', JSON.stringify(error, null, 2));
    // return Promise.reject(analyzeException(error.response.headers));
    // return Promise.reject(error);
    Message({
        title: '失败',
        message: '后台接口异常',
        type: 'error'
    });
    console.log(error, 'error');
    throw new Error(error);
});

const req = (url, params, method = 'GET', headerData = {}) => {
    return new Promise((resolve, reject) => {
        let config = {
            url: url,
            method: method,
            headers: headerData
        };

        if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
            Object.assign(config, {
                data: params
            });
        } else {
            Object.assign(config, {
                params: Object.assign({}, params, {
                    t: new Date().getTime()
                })
            });
        };

        const arrLink = [
            'cms/lottery',
            'cms/prize',
            'cms/level'
        ];
        const hasLink = (str) => {
            let res = false;
            str += '';
            arrLink.forEach(item => {
                if (str.indexOf(item) !== -1) {
                    res = true;
                };
            });
            return res;
        };
        axiosNew(config).then((data) => {
            console.log(data, '--------------------------------');
            if (data) {
                let resData;
                if ('errorCode' in data && hasLink(config.url)) {
                    resData = data;
                } else {
                    resData = data.data || data;
                }
                resolve(resData);
            } else {
                return false;
                // resolve(undefined);
            }
        }).catch(error => {
            reject(new Error(error));
        });
    });
};

export default req;

复制代码
// req-intercept.js
let requestList = []; // api请求记录

/** * 将当前请求进行去重 * @param {object} req 当前请求 * @param {array} requestList api请求记录 * @param {string} key 比对的信息key值 * @param {number} num 比对的个数 */
class repetition {
    constructor ({
        req,
        requestList,
        key,
        num
    }) {
        this.requestList = requestList;
        this.req = req;
        this.requestList.push(req);
        this.key = key;
        this.num = num || 1;
        this.cancelList = [];
    }
    cancelReq () {
        let count = 0;
        for (let i = 0; i < this.requestList.length; i++) {
            console.log(this.req[this.key], this.requestList[i][this.key]);
            if (this.req[this.key] === this.requestList[i][this.key]) {
                if (count > 0) {
                    this.requestList[i].funcCancel();
                    console.log('请求被释放');
                    this.cancelList.push(i);
                }
                count++;
            }
        }

        for (let j = 0; j < this.cancelList.length; j++) {
            this.requestList.splice(this.cancelList[j], 1);
        }
        this.cancelList = [];
        return count;
    }
}

/** * 将当前请求记录到缓存中 * @param {any} config 请求的内容 * @param {function} funcCancel 取消请求函数 */
export const add = (config, funcCancel) => {
    if (!config) {
        return false;
    }

    let obj = {
        req: genReq(config),
        funcCancel
    };
    let repetit = new repetition({
        req: obj,
        requestList,
        key: 'req'
    });
    repetit.cancelReq();
    // for (let index = 0, len = requestList.length; index < len; index++) {
    // if (req === requestList[index].req) {
    // console.error('-------执行funcCancel---------');
    // requestList[index].funcCancel();
    // // requestList.splice(index, 1); // 把这条记录从数组中移除
    // // console.log('cancel request:', config.url);
    // return;
    // }
    // }
};

/** * 将请求完成的对象从缓存中移除 * @param {any} config 请求对象 */
export const remove = (config) => {
    if (!config) {
        return false;
    }
    let req = genReq(config);
    for (let index = 0, len = requestList.length; index < len; index++) {
        if (req === requestList[index].req) {
            console.log('-- removed---');
            requestList.splice(index, 1); // 把这条记录从数组中移除
            break;
        }
    }
};

// 当前请求的api是否已有记录
export const has = (config) => {
    if (!config) {
        return false;
    }
    let req = genReq(config);
    for (let index = 0, len = requestList.length; index < len; index++) {
        if (req === requestList[index].req) {
            return true;
        }
    }
    return false;
};

/** * 生成请求记录对象,方面对比 * @param {object} config 请求对象 */
const genReq = (config) => {
    if (!config) {
        return '';
    }
    let arrayReq = [];
    arrayReq.push(`url:${config.url},`);
    arrayReq.push(`method:${config.method},`);
    arrayReq.push(`params:${json2Form(config.params)},`);
    arrayReq.push(`header:${json2Form(config.header)}`);
    // let key = 'Method: ' + config.method + ',Url: ' + url + ',Data: ' + json2Form(data) + ', header:' + json2Form(header);
    return arrayReq.join('');
};

const json2Form = (json) => {
    var str = [];
    for (var key in json) {
        if (key !== 't') {
            str.push(encodeURIComponent(key) + '=' + encodeURIComponent(json[key]));
        }
    }
    return str.join('&');
};
复制代码
相关文章
相关标签/搜索