axios 拦截器管理类链式调用手写实现及原理剖析

目录
  • axios库的拦截器使用
  • 整体设计
  • 拦截器管理类实现
    • 接口定义
    • 代码实现
  • 链式调用实现

axios库的拦截器使用

我们知道axios库的拦截器的使用方式如下:

// 添加一个请求拦截器
axios.interceptors.request.use(function (config) {
  // 在发送请求之前可以做一些事情
  return config;
}, function (error) {
  // 处理请求错误
  return Promise.reject(error);
});
// 添加一个响应拦截器
axios.interceptors.response.use(function (response) {
  // 处理响应数据
  return response;
}, function (error) {
  // 处理响应错误
  return Promise.reject(error);
});

在 axios 对象上有一个 interceptors 对象属性,该属性又有 request 和 response 2 个属性,它们都有一个 use 方法,use 方法支持 2 个参数,第一个参数类似 Promise.then 的 resolve 函数,第二个参数类似 Promise.then 的 reject 函数。我们可以在 resolve 函数和 reject 函数中执行同步代码或者是异步代码逻辑。

并且我们是可以添加多个拦截器的,拦截器的执行顺序是链式依次执行的方式。对于 request 拦截器,后添加的拦截器会在请求前的过程中先执行;对于 response 拦截器,先添加的拦截器会在响应后先执行。

axios.interceptors.request.use(config => {
  config.headers.test += '1'
  return config
})
axios.interceptors.request.use(config => {
  config.headers.test += '2'
  return config
})

此外,我们也可以支持删除某个拦截器,如下:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/})
axios.interceptors.request.eject(myInterceptor)

整体设计

我们先用一张图来展示一下拦截器工作流程:

整个过程是一个链式调用的方式,并且每个拦截器都可以支持同步和异步处理,我们自然而然地就联想到使用 Promise 链的方式来实现整个调用过程。

在这个 Promise 链的执行过程中,请求拦截器 resolve 函数处理的是 config 对象,而相应拦截器 resolve 函数处理的是 response 对象。

在了解了拦截器工作流程后,我们先要创建一个拦截器管理类,允许我们去添加 删除和遍历拦截器。

拦截器管理类实现

根据需求,axios 拥有一个 interceptors 对象属性,该属性又有 request 和 response 2 个属性,它们对外提供一个 use 方法来添加拦截器,我们可以把这俩属性看做是一个拦截器管理对象。

use 方法支持 2 个参数,第一个是 resolve 函数,第二个是 reject 函数,对于 resolve 函数的参数,请求拦截器是 AxiosRequestConfig 类型的,而响应拦截器是 AxiosResponse 类型的;而对于 reject 函数的参数类型则是 any 类型的。

根据上述分析,我们先来定义一下拦截器管理对象对外的接口。

接口定义

这里我们定义了 AxiosInterceptorManager 泛型接口,因为对于 resolve 函数的参数,请求拦截器和响应拦截器是不同的。

export interface AxiosInterceptorManager<T> {
  use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number
  eject(id: number): void
}
export interface ResolvedFn<T=any> {
  (val: T): T | Promise<T>
}
export interface RejectedFn {
  (error: any): any
}

代码实现

import { ResolvedFn, RejectedFn } from '../types'
interface Interceptor<T> {
  resolved: ResolvedFn<T>
  rejected?: RejectedFn
}
export default class InterceptorManager<T> {
  private interceptors: Array<Interceptor<T> | null>
  constructor() {
    // 拦截器数组
    this.interceptors = []
  }
  // 收集拦截器
  use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number {
    this.interceptors.push({
      resolved,
      rejected
    })
    return this.interceptors.length - 1
  }
  // 遍历用户写的拦截器,并执行fn函数把拦截器作为参数传入
  forEach(fn: (interceptor: Interceptor<T>) => void): void {
    this.interceptors.forEach(interceptor => {
      if (interceptor !== null) {
        fn(interceptor)
      }
    })
  }
  eject(id: number): void {
    if (this.interceptors[id]) {
      // 置为null,不能直接删除
      this.interceptors[id] = null
    }
  }
}

我们定义了一个 InterceptorManager 泛型类,内部维护了一个私有属性 interceptors,它是一个数组,用来存储拦截器。该类还对外提供了 3 个方法,其中 use 接口就是添加拦截器到 interceptors 中,并返回一个 id 用于删除;

forEach 接口就是遍历 interceptors 用的,它支持传入一个函数,遍历过程中会调用该函数,并把每一个 interceptor 作为该函数的参数传入;eject 就是删除一个拦截器,通过传入拦截器的 id 删除。

链式调用实现

当我们实现好拦截器管理类,接下来就是在 Axios 中定义一个 interceptors 属性,它的类型如下:

interface Interceptors {
  request: InterceptorManager<AxiosRequestConfig>
  response: InterceptorManager<AxiosResponse>
}
export default class Axios {
  interceptors: Interceptors
  constructor() {
    this.interceptors = {
      request: new InterceptorManager<AxiosRequestConfig>(),
      response: new InterceptorManager<AxiosResponse>()
    }
  }
}

Interceptors 类型拥有 2 个属性,一个请求拦截器管理类实例,一个是响应拦截器管理类实例。我们在实例化 Axios 类的时候,在它的构造器去初始化这个 interceptors 实例属性。

接下来,我们修改 request 方法的逻辑,添加拦截器链式调用的逻辑:

interface PromiseChain {
  resolved: ResolvedFn | ((config: AxiosRequestConfig) => AxiosPromise)
  rejected?: RejectedFn
}
request(url: any, config?: any): AxiosPromise {
  if (typeof url === 'string') {
    if (!config) {
      config = {}
    }
    config.url = url
  } else {
    config = url
  }
  // 定义一个数组,这个数组就是要执行的任务链,默认有一个真正发送请求的任务
  const chain: PromiseChain[] = [{
    resolved: dispatchRequest,
    rejected: undefined
  }]
  // 把用户定义的请求拦截器存放到任务链中,请求拦截器最后注册的最先执行,所以使用unshift方法
  this.interceptors.request.forEach(interceptor => {
    chain.unshift(interceptor)
  })
  // 把响应拦截器存放到任务链中
  this.interceptors.response.forEach(interceptor => {
    chain.push(interceptor)
  })
  // 利用config初始化一个promise
  let promise = Promise.resolve(config)
  // 遍历任务链
  while (chain.length) {
    // 取出任务链的首个任务
    const { resolved, rejected } = chain.shift()!
    // resolved的执行时机是就是上一个promise执行resolve()的时候,这样就形成了链式调用
    promise = promise.then(resolved, rejected)
  }
  return promise
}

首先,构造一个 PromiseChain 类型的数组 chain,并把 dispatchRequest 函数赋值给 resolved 属性;接着先遍历请求拦截器插入到 chain 的前面;然后再遍历响应拦截器插入到 chain 后面。

接下来定义一个已经 resolve 的 promise,循环这个 chain,拿到每个拦截器对象,把它们的 resolved 函数和 rejected 函数添加到 promise.then 的参数中,这样就相当于通过 Promise 的链式调用方式,实现了拦截器一层层的链式调用的效果。

注意我们拦截器的执行顺序,对于请求拦截器,先执行后添加的,再执行先添加的;而对于响应拦截器,先执行先添加的,后执行后添加的。

以上就是axios 拦截器管理类链式调用手写实现及原理剖析的详细内容,更多关于axios 拦截器管理类链式调用的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue封装Axios请求和拦截器的步骤

    PS:本文代码在vue-cli3构建的基础vue项目之上进行编写:vue create my-project axios 是一个基于 promise 的 http 库,可以用在浏览器和 node.js 中, 也是vue官方推荐使用的http库. axios很好用,其中之一就是它的拦截器十分强大,我们可以为请求和响应设置拦截器,比如请求拦截器可以在每个请求里加上token,做了统一处理后维护起来也方便,响应拦截器可以在接收到响应后先做一层操作,如根据状态码判断登录状态.授权. 之前初学的时候,经常

  • vue 导航守卫和axios拦截器有哪些区别

    在Vue项目中,有两种用户登录状态判断并处理的情况,分别为:导航守卫和axios拦截器. 一.什么是导航守卫? vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航.(在路由跳转时触发) 我们主要介绍的是可以验证用户登录状态的全局前置守卫,当一个导航触发时,全局前置守卫按照创建顺序调用.守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中. const router = new VueRouter({ ... })   router.beforeEac

  • vue中Axios添加拦截器刷新token的实现方法

    目录 1. Axios基本用法: 2. Axios基本封装用法: 3. 添加拦截器的用法 4. 注意事项: Axios是一款网络前端请求框架,基本用法如下: 1. Axios基本用法: const response = await Axios.create({ baseURL: "https://test.api.com", headers: { 'Content-Type': 'application/json', }, }).post<RequestResponse>(

  • Vue3 使用axios拦截器打印前端日志

    目录 一.前言 二.使用axios拦截器打印前端日志 一.前言 很多时候我们需要对前端进行调试,也就是前后端接口之间交互的调试,常用的方式肯定是打日志了,如console.log ('日志内容'). 就单个方法其实用这种方法是可以的,多个接口和方法,这样的调试方法就差了一些,再有就是方法有执行顺序,有时候反倒影响调试了. 二.使用axios拦截器打印前端日志 这是一种比较值得推荐的方式,也就是写一次,就不用总写console.log了. 突然想到,做测试时候,常看到的一句话: 一切都是为了测试

  • vue axios拦截器常用之重复请求取消

    引言 上一篇介绍了axios的简单封装,知道了axios拦截器的应用场景和方法,今天来看一下对于响应时间过长且请求次数过高的情况拦截器如何处理. 取消请求的方法 Axios使用内部提供的CancelToken来取消请求 官网示例1:用CancelToken.source工厂方法创建 cancel token,像这样 const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/use

  • vue+axios 拦截器实现统一token的案例

    需求 要想统一处理所有http请求和响应,就得用上 axios 的拦截器.通过配置 http response inteceptor ,当后端接口返回 401 Unauthorized(未授权) ,让用户重新登录. 通过这个项目学习如何实现一个前端项目中所需要的 登录及拦截.登出.token失效的拦截及对应 axios 拦截器的使用. 代码如下: const instance = axios.create({ baseURL: 'http://www.laravel5.5.com/api/',

  • axios 拦截器管理类链式调用手写实现及原理剖析

    目录 axios库的拦截器使用 整体设计 拦截器管理类实现 接口定义 代码实现 链式调用实现 axios库的拦截器使用 我们知道axios库的拦截器的使用方式如下: // 添加一个请求拦截器 axios.interceptors.request.use(function (config) { // 在发送请求之前可以做一些事情 return config; }, function (error) { // 处理请求错误 return Promise.reject(error); }); // 添

  • Vue基于vuex、axios拦截器实现loading效果及axios的安装配置

    准备 利用vue-cli脚手架创建项目 进入项目安装vuex.axios(npm install vuex,npm install axios) axios配置 项目中安装axios模块(npm install axios)完成后,进行以下配置: main.js //引入axios import Axios from 'axios' //修改原型链,全局使用axios,这样之后可在每个组件的methods中调用$axios命令完成数据请求 Vue.prototype.$axios=Axios l

  • 详解Vue中使用Axios拦截器

    需求是拦截前端的网络请求和相应. 废话不多说,直接上干货. 我用的是vue-cli3所以这个config文件是我自己创建的. 先介绍env.js //根据不同的环境更改不同的baseUrl let baseUrl = ''; //开发环境下 if (process.env.NODE_ENV == 'development') { baseUrl = ''; } else if (process.env.NODE_ENV == 'production') { baseUrl = '生产地址'; }

  • axios拦截器工作方式及原理源码解析

    目录 axios 拦截器的配置方式 use() 方法的定义 拦截器如何执行 拦截器回调方法的添加顺序 同步执行请求拦截器(顺序执行) 异步执行请求拦截器(同时执行) Q&A 拦截器是如何工作的 拦截器的执行顺序 同步&异步 axios 拦截器的配置方式 本文所用 axios 版本号为:1.3.2. axios 中有两种拦截器: axios.interceptors.request.use(onFulfilled, onRejected, options):配置请求拦截器. onFulfil

  • 详解给Vue2路由导航钩子和axios拦截器做个封装

    1.写在前面 最近在学习Vue2,遇到有些页面请求数据需要用户登录权限.服务器响应不符预期的问题,但是总不能每个页面都做单独处理吧,于是想到axios提供了拦截器这个好东西,再于是就出现了本文. 2.具体需求 用户鉴权与重定向:使用Vue提供的路由导航钩子 请求数据序列化:使用axios提供的请求拦截器 接口报错信息处理:使用axios提供的响应拦截器 3.简单实现 3.1 路由导航钩子层面鉴权与重定向的封装 路由导航钩子所有配置均在router/index.js,这里是部分代码 import

  • Vue 前端实现登陆拦截及axios 拦截器的使用

    该项目是利用了Github 提供的personal token作为登录token,通过token访问你的Repository List.通过这个项目学习如何实现一个前端项目中所需要的 登录及拦截.登出.token失效的拦截及对应 axios 拦截器的使用. 准备 你需要先生成自己的 Github Personal Token( 生成Token). Token 生成后 访问 Demo,即可查看你的Repository List. 项目结构 ├── README.md ├── dist // 打包构

  • Vux+Axios拦截器增加loading的问题及实现方法

    很多时候,我们在页面使用Ajax刷新时候,会希望它出现loading的图标,让用户体验更好一些.那么如果我们每次在Axios里面进行请求,都要显示loading图标的话,那么没有一个全局的方法的话,势必会造成代码冗余的问题.有什么方法可以实现这个方法呢? 这里,我们就要用到Axios的请求拦截器与相应拦截器了 首先,我们在请求拦截器里面增加一个VUX的loading组件 axios.interceptors.request.use( config => { //请求拦截器,调用loading插件

  • Vue中axios拦截器如何单独配置token

    在了解到cookie.session.token的作用后学习token的使用 cookie cookie是随着url将参数发送到后台,安全性最低,并且大小受限,不超过4kb左右,它的数据保存在客户端 session session数据保存在服务端,在内存中开辟空间存储数据,session文件名即sessionID保存在cookie内,随cookie传送到服务端后在服务端匹配session文件 token token是服务端的一种算法,如果登录成功,服务端就会根据算法生成一个字符串,将字符串传递回

  • vue下axios拦截器token刷新机制的实例代码

    //创建http.js文件,以下是具体代码: //引入安装的axios插件 import axios from 'axios' import router from '@/router'; import Vue from 'vue' const qs = require("qs"); let _this = new Vue(); let isLock = false; let refreshSubscribers = []; //判断token是否过期 function isToken

随机推荐