拦截器设计与实现

#需求分析

我们希望能对请求的发送和响应做拦截,也就是在发送请求之前和接收到响应之后做一些额外逻辑。

我们希望设计的拦截器的使用方式如下:

// 添加一个请求拦截器
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 的 resolve 函数,第二个参数类似 Promise 的 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 类型的。

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

#接口定义

types/index.ts

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 } 

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

#代码实现

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 } forEach(fn: (interceptor: Interceptor<T>) => void): void { this.interceptors.forEach(interceptor => { if (interceptor !== null) { fn(interceptor) } }) } eject(id: number): void { if (this.interceptors[id]) { this.interceptors[id] = null } } } 

我们定义了一个 InterceptorManager 泛型类,内部维护了一个私有属性 interceptors,它是一个数组,用来存储拦截器。该类还对外提供了 3 个方法,其中 use 接口就是添加拦截器到 interceptors中,并返回一个 id 用于删除;forEach 接口就是遍历 interceptors 用的,它支持传入一个函数,遍历过程中会调用该函数,并把每一个 interceptor 作为该函数的参数传入;eject 就是删除一个拦截器,通过传入拦截器的 id 删除。

#链式调用实现

本小节需要你对 Promise 掌握和理解,可以前往 mdn 学习。

当我们实现好拦截器管理类,接下来就是在 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 方法的逻辑,添加拦截器链式调用的逻辑:

core/Axios.ts

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 }] this.interceptors.request.forEach(interceptor => { chain.unshift(interceptor) }) this.interceptors.response.forEach(interceptor => { chain.push(interceptor) }) let promise = Promise.resolve(config) while (chain.length) { const { resolved, rejected } = chain.shift()! promise = promise.then(resolved, rejected) } return promise } 

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

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

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

#demo 编写

在 examples 目录下创建 interceptor 目录,在 interceptor 目录下创建 index.html:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="utf-8"> <title>Interceptor example</title> </head> <body> <script src="/__build__/interceptor.js"></script> </body> </html> 

接着创建 app.ts 作为入口文件:

import axios from '../../src/index'axios.interceptors.request.use(config => { config.headers.test += '1' return config }) axios.interceptors.request.use(config => { config.headers.test += '2' return config }) axios.interceptors.request.use(config => { config.headers.test += '3' return config }) axios.interceptors.response.use(res => { res.data += '1' return res }) let interceptor = axios.interceptors.response.use(res => { res.data += '2' return res }) axios.interceptors.response.use(res => { res.data += '3' return res }) axios.interceptors.response.eject(interceptor) axios({ url: '/interceptor/get', method: 'get', headers: { test: '' } }).then((res) => { console.log(res.data) }) 

该 demo 我们添加了 3 个请求拦截器,添加了 3 个响应拦截器并删除了第二个。运行该 demo 我们通过浏览器访问,我们发送的请求添加了一个 test 的请求 header,它的值是 321;我们的响应数据返回的是 hello,经过响应拦截器的处理,最终我们输出的数据是 hello13

至此,我们给 ts-axios 实现了拦截器功能,它是一个非常实用的功能,在实际工作中我们可以利用它做一些需求如登录权限认证。

我们目前通过 axios 发送请求,往往会传入一堆配置,但是我们也希望 ts-axios 本身也会有一些默认配置,我们把用户传入的自定义配置和默认配置做一层合并。其实,大部分的 JS 库都是类似的玩法。下面一章我们就来实现这个 feature。

转载于:https://www.cnblogs.com/QianDingwei/p/11403923.html

axios拦截器的实现相关推荐

  1. axios拦截器_78.1K 的 Axios 项目有哪些值得借鉴的地方

    Axios 是一个基于 Promise 的 HTTP 客户端,同时支持浏览器和 Node.js 环境.它是一个优秀的 HTTP 客户端,被广泛地应用在大量的 Web 项目中. 由上图可知,Axios ...

  2. 5 拦截器拦截请求路由_手写简易版axios拦截器,实现微信小程序wx.request的封装与拦截...

    前言: axios是一个功能强大的网络请求库,其中拦截器又是axios的精髓.在小程序的开发或者需要手动实现ajax的时候,没有实现对请求的拦截,开发的时候非常不方便,因此手写一个简易版的axios拦 ...

  3. axios vue 加载效果动画_vue中使用axios拦截器实现数据加载之前的loading动画显示 @劉䔳...

    首先新建一个 loading.vue组件,写loading动画效果 .loader { width: 100%; height: 100%; display: flex; align-items: c ...

  4. [js] axios拦截器原理是什么?

    [js] axios拦截器原理是什么? 拦截器原理其实就是用use添加用户自定义的函数到拦截器的数组中. 最后把他们放在拦截器请求前,请求后.组成promise链式调用. // 组成`Promise` ...

  5. 使用Axios拦截器打印前端请求日志和后端后返回日志

    在main.ts引入 import axios from 'axios'; axios.defaults.baseURL = process.env.VUE_APP_SERVER;/*** axios ...

  6. Vue2学习小记-给Vue2路由导航钩子和axios拦截器做个封装

    1.写在前面 最近在学习Vue2,遇到有些页面请求数据需要用户登录权限.服务器响应不符预期的问题,但是总不能每个页面都做单独处理吧,于是想到axios提供了拦截器这个好东西,再于是就出现了本文. 2. ...

  7. 【vue开发问题-解决方法】(八)利用axios拦截器实现elementUI中加载动画,控制加载区域

    [vue开发问题-解决方法](八)利用axios拦截器实现elementUI中加载动画,控制加载区域 参考文章: (1)[vue开发问题-解决方法](八)利用axios拦截器实现elementUI中加 ...

  8. Vue2学习小记-给Vue2路由导航钩子和axios拦截器做个封装 1

    1.写在前面 最近在学习Vue2,遇到有些页面请求数据需要用户登录权限.服务器响应不符预期的问题,但是总不能每个页面都做单独处理吧,于是想到axios提供了拦截器这个好东西,再于是就出现了本文. 2. ...

  9. vue2中使用axios,以及axios拦截器的配置

    目录 一.vue2项目中如何实现异步请求 1.axios:是一个基于Promise的网络请求库.既可以在node.js(服务器端)使用,也可以在浏览器端使用 2.vue中的使用方法 ​ (2)引用方法 ...

  10. axios拦截器、ElementUI组件的使用

    一.axios拦截器 1.axios模块的作用 是对基于http请求的封装.在浏览器对异步请求对象XMLHttpRequest进行封装 2.拦截器 ​ (1)请求拦截器:对客户端发起的请求进行统一的前 ...

最新文章

  1. 新建异常并处理java_java – 动态创建异常的工厂模式
  2. AndroidStudio导入项目一直卡在Building gradle project info最快速解决方案
  3. 私家车合乘系统 matlab,私家车贴上邻里合乘标志 武汉探路拼车合法化
  4. JZOJ__Day 1:【NOIP普及模拟】JABUKE
  5. transient-java 关键字
  6. RocketMQ如何保证消息顺序消费?又为何不解决消息重复消费问题?
  7. 贾跃亭:FF未来主义者们最终所要创造的社会价值 一定会得到资本市场的认可...
  8. iSaver for Mac(动态桌面屏保软件)
  9. 机器学习、数据科学、深度学习、可视化常用方法速查手册总结分享
  10. pyodbc 连接informix
  11. jsp页面中插入jsp代码片段
  12. html css 窗口样式,简单的css样式网页
  13. Java版九宫格算法
  14. PMP备考大全:经典题库(8月第4周)
  15. Semantic Proximity Search on Heterogeneous Graph by Proximity Embedding
  16. C#和JQ判断移动端还是PC端
  17. 中国十大险峻山路:最弯的公路,7公里68个拐(组图)(ZT)
  18. Python解析证书结构方法
  19. virtio-balloon 前后端源代码简单分析
  20. 19c打补丁简易步骤

热门文章

  1. Java处理文件BOM头的方式推荐
  2. Client does not support authentication protocol requested by server;
  3. Quaruts II 增量编译
  4. TSQL与PL/SQL的比较
  5. Express实现路由分发控制、RESTful API
  6. JavaScript 面向对象的程序设计1
  7. koa --- [MVC实现之一]自定义路由读取规则
  8. 虹软免费人脸识别SDK注册指南
  9. Linux网络/firewalld和netfilter/netfilter/iptables语法
  10. 《编程原本 》一3.3 程序变换