问题

在进行 axios 封装的时候,遇到个问题,就是每次发起请求时axios 都会执行两次响应拦截,甚是纳闷,一时理不出思路来。

代码如下:

class Http {
    constructor(config) {
        this.axios = axios;
        this.axiosInterceptor = undefined;
        // 公共的 header
        let defaultHeaders = {
            'Content-Type': 'application/json;charset=UTF-8',
            'Accept': 'application/json', // 通过头指定,获取的数据类型是JSON 'application/json, text/plain, */*',
            'x-end-point': '.10.'
        }
        if(config?.headers){
            for (let i in config.headers) {
                defaultHeaders[i] = config.headers[i];
            }
        }
        axios({
            // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
            // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
            baseURL: config?.baseURL,
            // `url` 是用于请求的服务器 URL
            url: config?.url,
            // `method` 是创建请求时使用的方法
            method: config?.method || 'get',
            // `headers` 是即将被发送的自定义请求头
            headers: {...defaultHeaders},
            // `params` 是即将与请求一起发送的 URL 参数
            // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
            params: config?.method === 'get' ? config?.params || {} : {},
            // `paramsSerializer` 是一个负责 `params` 序列化的函数
            // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
            paramsSerializer: function(params) {
                return Qs.stringify(params, {arrayFormat: 'brackets'})
            },
            // `data` 是作为请求主体被发送的数据
            // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
            // 在没有设置 `transformRequest` 时,必须是以下类型之一:
            // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
            // - 浏览器专属:FormData, File, Blob
            // - Node 专属: Stream
            data: config?.method === 'post' ? config?.params || {} : {},
            // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
            // 如果请求话费了超过 `timeout` 的时间,请求将被中断
            timeout: 0,
            // `withCredentials` 表示跨域请求时是否需要使用凭证
            withCredentials: false, // default 为true则产生跨域,跨域携带cookie
            // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
            responseType: 'json', // default
        });
        // 添加请求拦截器
        axios.interceptors.request.use( (config) =>  {
            // 在发送请求之前做些什么
            return config;
        }, function (error) {
            // 对请求错误做些什么
            return Promise.reject(error);
        });
        // 添加响应拦截器
        axios.interceptors.response.use((res) => {
            const { status, data } = res;
            // 对错误状态提示进行处理
            let message = ''
            if (status < 200 || status >= 300) {
                // 处理http错误,抛到业务代码
                message = this.showResState(status)
                if (typeof res.data === 'string') {
                    res.data = {code:status, message }
                } else {
                    res.data.code = status
                    res.data.message = message
                }
            }
        return res.data;
        }, function (error) {
            // 对响应错误做点什么
            return Promise.reject(error);
        });
    }
    get(url,params={}){
        // 为给定 ID 的 user 创建请求
        return new Promise((resolve, reject) => {
            this.axios.get(url,{
                params
            }).then(response => {
                // 2. 如果成功了, 调用resolve(value)
                resolve(response);
            })
                .catch(error => {
                    // 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
                    reject(error)
                    // message.error('请求出错了: '   error.message).then(r => {});
                }).finally(() => {
            })
        });
    }
    post(url,params={}){
        return new Promise((resolve, reject) => {
            this.axios.post(url, params).then(response => {
                // 2. 如果成功了, 调用resolve(value)
                resolve(response);
            })
                .catch(error => {
                    // 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
                    reject(error)
                    // message.error('请求出错了: '   error.message).then(r => {});
                }).finally(() => {
            })
        });
    }
    showResState (state) {
        let message = '';
        // 这里只做部分常见的示例,具体根据需要进行配置
        switch (state) {
            case 400:
                message = '请求错误(400)'
                break
            case 401:
                message = '未授权,请重新登录(401)'
                break
            case 403:
                message = '拒绝访问(403)'
                break
            case 404:
                message = '请求出错(404)'
                break
            case 500:
                message = '服务器错误(500)'
                break
            case 501:
                message = '服务未实现(501)'
                break
            case 502:
                message = '网络错误(502)'
                break
            case 503:
                message = '服务不可用(503)'
                break
            default:
                message = `连接出错(${state})!`
        }
        return `${message},请检查网络或联系网站管理员!`
    }
    // 插件初始化时会传入所需的配置项
    autoAddToken (config) {
        // 在请求阶段时修改 config 配置项为其添加 token 具体属性名称可自定义
        config.headers ??= {}
        config.headers.Authorization = localStorage.token || null
        return config
    }
}
export default Http;

可能遇到过该类问题的小伙伴们一眼就看出问题所在,对于未有碰到过这个问题的小伙伴,可能就又点煎熬了。

原因

若你使用use,就像Node.js里的use那样,会不断地往axios对象添加interceptors,由于我将该拦截器放在函数内,只要函数被执行,则会再次将拦截器函数增添到axios对象上。

所以,推荐的办法是,将拦截器放在函数外,可我的需求决定了,我必须将它放在函数内,那么该如何解决呢?

解决

添加该文件内的唯一变量标识符let interceptor = null,进行判断,只要拦截器存在,则不会继续添加,部分代码如下所示:

if (!this.interceptor) {
// 添加响应拦截器
this.interceptor = axios.interceptors.response.use((res) => {
        const { status, data } = res;
        // 对错误状态提示进行处理
        let message = ''
        if (status < 200 || status >= 300) {
            // 处理http错误,抛到业务代码
            message = this.showResState(status)
            if (typeof res.data === 'string') {
                res.data = {code:status, message }
            } else {
                res.data.code = status
                res.data.message = message
            }
        }
    return res.data;
    }, function (error) {
        // 对响应错误做点什么
        return Promise.reject(error);
    });
  }

奈何不好使。不得一不得不把拦截器提取到类外部,问题解决。

这里只贴部分主要代码:

import axios from "axios";
/* 将拦截器 置于封装类之外 */
// 添加请求拦截器
axios.interceptors.request.use( (config) =>  {
    // 在发送请求之前做些什么 添加 token 等鉴权功能
    //...
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use((res) => {
    const { status } = res;
    // 在发送结果之前做些什么  对错误状态提示进行处理
    //...
    return res.data;
}, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
});
class Http {
    constructor(config) {
        this.axios = axios;
        // 这里仍需对配置进行处理,代码省略了
        this.config = config;
    }
    // Get 请求
    get(url,params={},headers={}){
        // ...
    }
    // POST 请求
    post(url,params={},headers={}){
        // ...
    }
}
export default Http;
// 无特殊需求的只需使用这个一个对象即可 公共 header 可在此配置, 如需多个实例 可按照此方式创建多个进行导出
export const Axios = new Http({
    headers: {
        'x-http-token': 'xxx'
    }
});

这里不对具体的方法进行描述,只做一个解决问题的说明,后续会针对 axios 类的封装,单独写篇文章再详细说明下的

以上就是axios的interceptors多次执行问题解决的详细内容,更多关于axios interceptors多次执行的资料请关注Devmax其它相关文章!

axios的interceptors多次执行问题解决的更多相关文章

  1. 解决axios:"timeout of 5000ms exceeded"超时的问题

    这篇文章主要介绍了解决axios:"timeout of 5000ms exceeded"超时的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  2. vue日常开发基础Axios网络库封装

    这篇文章主要为大家介绍了vue日常开发基础Axios网络库封装示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. 简单聊一聊axios配置请求头content-type

    最近在工作中碰到一个问题,后端提供的get请求的接口需要在request header设置,下面这篇文章主要给大家介绍了关于axios配置请求头content-type的相关资料,需要的朋友可以参考下

  4. vue中Axios的封装和API接口的管理示例详解

    这篇文章主要介绍了vue中Axios的封装和API接口的管理,主要目的就是在帮助我们简化代码和利于后期的更新维护,本文结合实例代码给大家介绍的非常详细,需要的朋友可以参考下

  5. vue+axios methods方法return取不到值问题及解决

    这篇文章主要介绍了vue+axios methods方法return取不到值问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  6. vue3学习笔记简单封装axios示例实现

    这篇文章主要为大家介绍了vue3学习笔记简单封装axios示例实现,

  7. Vue使用axios发送请求并实现简单封装的示例详解

    这篇文章主要介绍了Vue使用axios发送请求并实现简单封装,主要包括安装axios及简单使用配置方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  8. axios中如何进行同步请求(async+await)

    这篇文章主要介绍了axios中如何进行同步请求(async+await),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  9. node+express+axios实现单文件上传功能

    这篇文章主要为大家详细介绍了node+express+axios实现单文件上传功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Axios和Jquery实现文件上传功能

    这篇文章主要为大家详细介绍了Axios+Jquery实现文件上传功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部