我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何实现axios的自定义适配器adapter

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何实现axios的自定义适配器adapter

1. 适配器要实现的功能

我们在基于 axios 实现额外的数据模块时,应当与 axios 的模式进行对齐。因此在返回的数据格式上,实现的功能上尽量保持一致。

1.1 promise 和工具

所有的适配均应当实现为 Promise 方式。

而且,有些功能的实现,axios 将其下放到了适配器中自己进行实现,例如

  • url 的拼接:即 baseURL 和 url 的拼接,若存在 baseURL 且 url 为相对路径,则进行拼接,否则直接使用 url;
  • 参数的拼接:若是 get 请求,需要自行将 object 类型拼接为 url 参数的格式并与 url 拼接完成;

这是自己需要实现的两个基本的工具方法。

1.2 响应的格式

这里我们要注意到请求接口正常和异常的格式。

接口正常时:


const result = {
    status: 200, // 接口的http 状态
    statusText: 'ok',
    config: 'config', // 传入的config配置,原样返回即可,方便在响应拦截器和响应结果中使用
    data: {}, // 真实的接口返回结果
};

接口异常时,我们可以看下 axios 源码中对错误信息的处理createError,enhanceError(createError 中调用了 enhanceError),首先会创建一个 error 实例,然后给这个 error 实例添加一个属性:


module.exports = function enhanceError(error, config, code, request, response) {
    error.config = config;
    if (code) {
        error.code = code;
    }

    error.request = request;
    error.response = response;
    error.isAxiosError = true;

    error.toJSON = function toJSON() {
        return {
            // Standard
            message: this.message,
            name: this.name,
            // Microsoft
            description: this.description,
            number: this.number,
            // Mozilla
            fileName: this.fileName,
            lineNumber: this.lineNumber,
            columnNumber: this.columnNumber,
            stack: this.stack,
            // Axios
            config: this.config,
            code: this.code,
        };
    };
    return error;
};

可以看到,除了正常的错误信息外,还加入了很多别的属性,例如 request, response, config 等。这里我们在自己实现适配器时,最好也要这样统一编写,方便更上层的业务层统一处理,避免为单独的适配器进行特殊处理。

关于 1.1 和 1.2 中的内容,若不进行打包编译,则需要自己实现。若还要通过 webpack 等打包工具编译一下的,可以直接引用 axios 中的方法,不用自己实现了,参考官方基于 axios 实现的mock-axios。例如:


import axios from 'axios';
import buildURL from 'axios/lib/helpers/buildURL';
import isURLSameOrigin from 'axios/lib/helpers/isURLSameOrigin';
import btoa from 'axios/lib/helpers/btoa';
import cookies from 'axios/lib/helpers/cookies';
import settle from 'axios/lib/core/settle';
import createError from 'axios/lib/core/createError';

然后直接使用就行了,不用再进行二次开发。

1.3 超时设置

我们不能无限地等待第三方服务的响应,如果第三方服务无响应或者响应时间过长,应当适时的终止掉。在 axios 中,前端使用了XMLHttpRequest,在 node 端使用了http,来实现接口的请求,两者都有超时的设定,可以设置 timeout 字段来设置超时的时间,自动取消当前的请求。

像有的发起的请求,自己并没有超时的设定,例如 jsonp,是用创建一个 script 标签来发起的请求,这个请求必须等到服务器有响应才会终止(成功或者失败)。这时,就需要我们自己用一个setTimeout来模拟了,但这样,即使返回给业务层说“超时了,已取消当前请求”,但实际上请求还在,只不过若超过规定时间,只是不再执行对应的成功操作而已。

1.4 主动取消请求

我们也会有很多并没有到超时时间,就需要主动取消当前请求的场景,例如在请求返回之前就切换了路由;上次请求还没响应前,又需要发出新的请求等。都需要主动地取消当前请求。

axios 中已经提供了取消请求的功能,我们只需要按照规则接入即可。我们来看下 XMLHttpRequest 请求器中是怎么取消请求的,在写自定义请求器时也可以照理使用。

在lib/adapters/xhr.js#L158中:


// 若config中已经配置了cancelToken
if (config.cancelToken) {
    // Handle cancellation
    // 若在外城执行了取消请求的方法,则这里将当前的请求取消掉
    config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
            return;
        }

        // xhr中使用abort方法取消当前请求
        request.abort();
        reject(cancel);
        // Clean up request
        request = null;
    });
}

我们在写自己的适配器时,也可以将这段拷贝过去,将内部取消的操作更换为自己的即可。

到这里,若把上面的功能都实现了,就已经完成了一个标准的适配器了。

2. 编写自定义适配器

每个人需要的适配器肯定也不一样,复杂度也不一样,例如有的想接入小程序的请求,我自己想接入客户端里提供的数据请求方式等。我们这里只是通过实现一个简单的jsonp适配器来讲解下实现方式。

我们以 es6 的模块方式来进行开发。所有的实现均在代码中进行了讲解。


// 这里的config是axios里所有的配置
const jsonpAdapter = (config) => {
    return new Promise((resolve, reject) => {
        // 是否已取消当前操作
        // 因jsonp没有主动取消请求的方式
        // 这里使用 isAbort 来标识
        let isAbort = false;

        // 定时器标识符
        let timer = null;

        // 执行方法的名字,
        const callbackName = `jsonp${Date.now()}_${Math.random()
            .toString()
            .slice(2)}`;

        // 这里假设已经实现了baseURL和url的拼接方法
        const fullPath = buildFullPath(config.baseURL, config.url);

        // 这里假设已经实现了url和参数的拼接方法
        // 不太一样的地方在于,jsonp需要额外插入一个自己的回调方法
        const url = buildURL(
            fullPath,
            {
                ...config.params,
                ...{ [config.jsonpCallback || 'callback']: callbackName },
            },
            config.paramsSerializer
        );

        // 创建一个script标签
        let script = document.createElement('script');

        // 成功执行操作后
        function remove() {
            if (script) {
                script.onload = script.onerror = null;

                // 移除script标签
                if (script.parentNode) {
                    script.parentNode.removeChild(script);
                }
                // 取消定时器
                if (timer) {
                    clearTimeout(timer);
                }

                script = null;
            }
        }

        // 成功请求后
        window[callbackName] = (data) => {
            // 若已需要请求,则不再执行
            if (isAbort) {
                return;
            }

            // 返回的格式
            const response = {
                status: 200,
                statusText: 'ok',
                config,
                request: script,
                data: data,
            };
            remove();
            // 实际上这里上一个settle操作,会额外判断是否是合理的status状态
            // 若我们在config.validateStatus中设置404是合理的,也会进入到resolve状态
            // 但我们这里就不实现这个了
            // settle(resolve, reject, response);
            resolve(response);
        };

        // 请求失败
        script.onerror = function (error) {
            remove();

            reject(createError('Network Error', config, 404));
        };

        // 若设置了超时时间
        if (config.timeout) {
            timer = setTimeout(function () {
                remove();
                // 取消当前操作
                isAbort = true;
                reject(
                    createError(
                        'timeout of ' + config.timeout + 'ms exceeded',
                        config,
                        405
                    )
                );
            }, config.timeout);
        }

        // 若定义了取消操作
        if (config.cancelToken) {
            config.cancelToken.promise.then(function () {
                if (!script) {
                    return;
                }
                remove();
                isAbort = true;

                reject(createError('Cancel Error', config, 404));
            });
        }

        script.class="lazy" data-src = url;
        const target =
            document.getElementsByTagName('script')[0] || document.head;
        target.parentNode && target.parentNode.insertBefore(script, target);
    });
};

export default jsonpAdapter;

3. 将适配器添加到 axios 中

axios 的 config 提供了 adapter 字段让我们插入自己的适配器。使用自定义适配器又有两种情况:

1.完全只使用自定义的适配器;

2.在某种情况下使用自定义适配器,其他情况时还是使用 axios 自己的适配器。

第 1 种情况还好,只需要 return 自己适配器返回的结果结果即可;而第 2 种情况中,则有个小坑需要踩一下,我们这里也只讲解下第 2 种情况。我要把刚才实现的 jsonp 适配器添加到 axios 中,并且只在参数有format=jsonp时才调用该适配器,其他还是用的 axios 提供的适配器。


import Axios from 'axios';
import jsonpAdapter from './jsonpAdater';

const request = Axios.create({
    adapter: (config) => {
        if (config?.params?.format === 'jsonp') {
            return jsonpAdapter(config);
        }

        // 这里需要将config.adapter设置为空
        // 否则会造成无限循环
        return defaultAxios({ ...config, ...{ adapter: undefined } });
    },
});

使用自定义的适配器 jsonp 发起请求。


// 使用自定义的适配器jsonp发起请求
var options = {
    params: {
        format: 'jsonp',
    },
};
request(
    'https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336',
    options
)
    .then(function (response) {
        console.log('jsonp response', response);
    })
    .catch(function (error) {
        console.error('jsonp error', error);
    });

使用 axios 默认的适配器发起请求。


// 使用axios默认的适配器发起请求
request('https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336')
    .then(function (response) {
        console.log('axios response', response);
    })
    .catch(function (error) {
        console.error('axios error', error);
    });

4. 总结

这里,我们就已经实现了一个自定义适配器了,在满足一定条件时可以触发这个适配器。通过这个思路,我们也可以实现一个自定义的 mock 方法,例如当参数中包含format=mock时则调用 mock 接口,否则就正常请求。

以上就是如何实现axios的自定义适配器adapter的详细内容,更多关于axios自定义适配器adapter的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何实现axios的自定义适配器adapter

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

使用axios怎么一个自定义适配器adapter

使用axios怎么一个自定义适配器adapter?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1. 适配器要实现的功能我们在基于 axios 实现额外的数据模块时,应当与
2023-06-15

Android之自定义实现BaseAdapter(通用适配器一)

通过前面的优化布局之后,我们接着来讲如何打造一个通用的适配器,那么通用适配器能干吗呢?很简单,减少我们对代码的书写,下面开始上代码了。 MyAdapter.javapublic class MyAdapter extends BaseAda
2022-06-06

Android之自定义实现BaseAdapter(通用适配器二)

在上一篇通用适配器一中,我们已经把ViewHolder抽取为了一个通用的持有类,极大程度上减少了我们对代码的书写,现在开始在那样的基础上在此抽取,从而达到更优。先回顾上一篇中的MyAdapter.java类中的代码,然后通过这个代码我们再一
2022-06-06

Android之自定义实现BaseAdapter(通用适配器三)

在上一篇中,我们说过,在setData中如果有很多控件的话,我们还是要在该方法中写入很多代码,为了降低开发的方便性,本次就在此基础上再一次优化。实现原理是这样的,每次在setData中都要查找控件,然后setXXX()什么的,我们可以把这写
2022-06-06

C++中使用FFmpeg适配自定义编码器的实现方法

本文介绍了在C++中使用FFmpeg库进行自定义编码器适配的实现方法。文章通过具体的代码示例,介绍了FFmpeg的基本使用方法和自定义编码器的实现过程,帮助读者了解如何在C++中进行音视频编码和解码的开发工作,并能够实现自定义的编码器适配
2023-05-16

spring boot如何实现自定义配置源

这篇文章给大家分享的是有关spring boot如何实现自定义配置源的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。概述我们知道,在Spring boot中可以通过xml或者@ImportResource 来引入自
2023-05-30

Android列表实现(3)_自定义列表适配器思路及实现代码

下面的例子为使用自定义的列表适配器来显示列表。 代码如下: View Code import android.os.Bundle; import android.app.ListActivity; import android.conte
2022-06-06

Android编程中常用适配器及自定义适配器用法实例分析

本文实例讲述了Android编程中常用适配器及自定义适配器用法。分享给大家供大家参考,具体如下: 一、适配器. 顾名思义,就是把一些数据给弄得适当,适合以便于在View上显示。可以看作是界面数据绑定的一种理解。它所操纵的数据一般都是一些比较
2022-06-06

Android Compose自定义TextField如何实现自定义的输入框

这篇文章主要介绍Android Compose自定义TextField如何实现自定义的输入框,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!简单自定义BasicTextField示例1.代码 var text by r
2023-06-29

Android中 自定义数据绑定适配器BaseAdapter的方法

代码如下:public class PersonAdapter extends BaseAdapter { private List persons;// 要绑定的数据 private int resource;// 绑定的一个条目界面的i
2022-06-06

php如何实现自定义url

这篇文章将为大家详细讲解有关php如何实现自定义url,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。php实现自定义url的方法:1、通过mod_rewrite配置htaccess文件实现URL重写;2、
2023-06-20

Springboot如何实现自定义mybatis拦截器

这篇文章将为大家详细讲解有关Springboot如何实现自定义mybatis拦截器,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。实践的准备 : 整合mybatis ,然后故意写了3个查询方法, 1个是li
2023-06-22

Java SpringMVC如何实现自定义拦截器

本篇内容主要讲解“Java SpringMVC如何实现自定义拦截器”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java SpringMVC如何实现自定义拦截器”吧!SpringMVC实现自定义
2023-06-29

C#如何实现自定义屏保

这篇文章主要介绍“C#如何实现自定义屏保”,在日常操作中,相信很多人在C#如何实现自定义屏保问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#如何实现自定义屏保”的疑惑有所帮助!接下来,请跟着小编一起来学习吧
2023-07-04

编程热搜

目录