通过axios拦截器完成api上报

背景

之前需要写一个api上报的功能,主要是思路是使用axios拦截器中上报API的相关信息。那么我们就来分析下整体方案的实现。

方案

流程图

首先我们先看一下整体设计的流程图:

主要代码示例

根据上面的流程图我们来看下代码的实现:

javascript 复制代码
export default API = (axiosInstance) => {
  const TimerMaxDuration = 6000; // 自定义超时时间
  // axiosInstance 是传进来的axios实例
  axiosInstance.request.use((config) => {
      // ...
      config.__apiId = id; // 添加唯一标识 id是根据某种规则生成
      const msg = {
          id,
          begin: Date.now(),
          timer: null
      };
      msg.timer = setTimeout(() => {
          // 上报逻辑 
          ...
          // 清除相应配置
          if (msg.timer) clearTimeout(msg.timer); 
          this._requests.delete(id);
      }, TimerMaxDuration);
      this._requests.set(id, msg);
      return config;
   }, (err) => {
      // ...
      return Promise.reject(err);
  });
  axiosInstance.response.use((res) => {
      // ...
      const id = res.config.__apiId || '';
      const msg = this._requests.get(id);
      if(msg) {
          // 上报逻辑
          ...
          // 清除相应配置
          if (msg.timer) clearTimeout(msg.timer); 
          this._requests.delete(id);
      }
      return res;
   }, (err) => {
          // ...
      return Promise.reject(err);
  });
}

代码解析

  • __apiId是根据自定义规则生成唯一标识;
  • TimerMaxDuration 是自定义超时时间,使用setTimeout处理超时逻辑;
  • 没有超时的情况就在response拦截器中上报处理;
  • 上报完成后清除相关配置,避免上报多次。

应用

现在我们已经可以对api上报处理了,就可以应用到我们的项目中。

arduino 复制代码
// request.js
import axios from 'axios';
// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 50000, // 请求超时时间
});
new API(repoter,service);

至此,我们已经实现整个方案。

完结,撒花~

当我准备开心的下班的时候,同事小强说:我们这里了上报的数据怎么有问题呢,在数据平台上怎么没有看到我们的系统的数据...

那么我们继续往下看看都是遇到了啥问题~

可能遇到问题

当存在多个拦截器

在业务中,很多情况会用axios拦截器来处理统一的业务逻辑,或者对返回的数据进行统一处理等等。那么多个拦截器的情况axios是怎么处理的,我们的api拦截器和业务拦截器之间又有什么影响呢?

接下来主要来分析下axios是如何实现拦截器的,打开axios的源码,找到axios/lib/core/Axios.js文件。

javascript 复制代码
// axios/lib/core/Axios.js
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

Axios的实例化对象,里面有interceptors对象,里面有request和response属性,都是InterceptorManager实例化的对象,我们接着找到axios/lib/core/InterceptorManager.js文件.

javascript 复制代码
// axios/lib/core/InterceptorManager.js
function InterceptorManager() {
  this.handlers = [];
}

// 每个use都是维护一个handlers数据,每一项都包含fulfilled,rejected
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};
// 拦截器的forEach方法,遍历handlers每一项
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

从上面可以看到,use是定义在构造函数InterceptorManager的原型上的,我们定义的完成和失败的两个回调函数被push到一个handlers数组中去了,那么这个handlers数组到底有什么意义呢?我们再回过头来看 axios/lib/core/Axios.js 文件中下面的代码。

javascript 复制代码
Axios.prototype.request = function(config) {
  // 对config进行处理的代码还是省略掉
  // ... 
  // Hook up interceptors middleware
   
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);
  // 请求拦截 遍历request的handlers,将每一项 fulfilled,rejected方法 加入到chain的首部
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
 // 返回拦截 遍历response的handlers,将每一项 fulfilled,rejected方法 加入到chain的尾部
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
}

从上面的代码我们知道了,整个请求是一个promise,整个链是按照先执行了request的fufilled(config)和rejected(err),这个promise执行并且resolve后才会去执行dispatchRequest(config),最后执行response的fulfilled和rejected。

  • 对于request的interceptor是先进后执行,而对于response的interceptor是先进先执行
  • request和response的拦截器都可以有多对,其中每一个点都会挂在一个then()的调用上,promise.then(chain.shift(), chain.shift());

那么回到我们的问题中当存在多个拦截器interceptor1, interceptor2,那么执行顺序是

总结

当在业务中有对respons返回的数据做处理,那么最好是最后一个调用,也就是说我们的api上报拦截需要在业务拦截处理之前,这样api上报的数据是完整的。

相关推荐
胡西风_foxww2 分钟前
【ES6复习笔记】数值扩展(16)
前端·笔记·es6·扩展·数值
mosen8684 分钟前
uniapp中uni.scss如何引入页面内或生效
前端·uni-app·scss
白云~️4 分钟前
uniappX 移动端单行/多行文字隐藏显示省略号
开发语言·前端·javascript
沙尘暴炒饭6 分钟前
uniapp 前端解决精度丢失的问题 (后端返回分布式id)
前端·uni-app
昙鱼20 分钟前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
天天进步201526 分钟前
Vue项目重构实践:如何构建可维护的企业级应用
前端·vue.js·重构
小华同学ai29 分钟前
vue-office:Star 4.2k,款支持多种Office文件预览的Vue组件库,一站式Office文件预览方案,真心不错
前端·javascript·vue.js·开源·github·office
APP 肖提莫30 分钟前
MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
java·前端·算法
问道飞鱼42 分钟前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
k093343 分钟前
vue中proxy代理配置(测试一)
前端·javascript·vue.js