拦截器设计与实现

#需求分析

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

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

// 添加一个请求拦截器

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 {

use(resolved: ResolvedFn, rejected?: RejectedFn): number

eject(id: number): void

}

export interface ResolvedFn {

(val: T): T | Promise

}

export interface RejectedFn {

(error: any): any

}

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

#代码实现

import { ResolvedFn, RejectedFn } from '../types'

interface Interceptor {

resolved: ResolvedFn

rejected?: RejectedFn

}

export default class InterceptorManager {

private interceptors: Array | null>

constructor() {

this.interceptors = []

}

use(resolved: ResolvedFn, rejected?: RejectedFn): number {

this.interceptors.push({

resolved,

rejected

})

return this.interceptors.length - 1

}

forEach(fn: (interceptor: Interceptor) => 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

response: InterceptorManager

}

export default class Axios {

interceptors: Interceptors

constructor() {

this.interceptors = {

request: new InterceptorManager(),

response: new InterceptorManager()

}

}

}

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:

Interceptor example

接着创建 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。

axios拦截器 config_axios拦截器的实现相关推荐

  1. axios拦截器 config_axios拦截器

    页面发送http请求,很多情况我们要对请求和其响应进行特定的处理:如果请求数非常多,单独对每一个请求进行处理会变得非常麻烦,程序的优雅性也会大打折扣.好在强大的axios为开发者提供了这样一个API: ...

  2. axios 最全 请求拦截器 响应拦截器 配置公共请求头 超时时间 以及get delete post put 四种请求传参方式

    axios 拦截器 请求拦截器 请求拦截器的作用是在请求发送前进行一些操作 例如在每个请求体里加上token,统一做了处理如果以后要改也非常容易 响应拦截器 响应拦截器的作用是在接收到响应后进行一些操 ...

  3. axios 请求拦截器响应拦截器

    一. 拦截器介绍 一般在使用axios时,会用到拦截器的功能,一般分为两种:请求拦截器.响应拦截器. 请求拦截器 在请求发送前进行必要操作处理,例如添加统一cookie.请求体加验证.设置请求头等,相 ...

  4. vue 无法进入response拦截器_vue拦截器的一次实践

    起因 最近在做一个项目前端框架使用的是vue,项目接近尾声的时候发现需要增加一个对所有的http请求过滤的功能,所有的请求需要加上token再发送给服务器,服务器根据token判断用户身份是否有效,响 ...

  5. jfinal js 拦截_jfinal 使用拦截器处理繁琐的前置条件判定

    使用拦截器处理繁琐的前置条件判定 背景 在开发过程中,为了提高程序的健壮性,对参数的校验是必不可少的,然而使用传统的方式进行参数校验时,导致程序中存在了if xxx return xxx;处理不够优雅 ...

  6. springboot拦截器拦截提示_Springboot拦截器使用及其底层源码剖析

    博主最近看了一下公司刚刚开发的微服务,准备入手从基本的过滤器以及拦截器开始剖析,以及在帮同学们分析一下上次的jetty过滤器源码与本次Springboot中tomcat中过滤器的区别.正题开始,拦截器 ...

  7. springmvc拦截器无法拦截jsp

    为什么80%的码农都做不了架构师?>>>    问题:spring mvc的拦截器只拦截controller不拦截jsp文件,如果不拦截jsp文件也会给系统带安全性问题. 解决方案: ...

  8. Struts2 自定义拦截器(方法拦截器)

    转自:http://05061107cm.iteye.com/blog/365504 struts2系统自带了很多拦截器,有时需要我们自己定义,一般有两种方式: 一.实现Interceptor接口 J ...

  9. Struts2 拦截器: 拦截器与过滤器区别

    1.首先要明确什么是拦截器.什么是过滤器 1.1 什么是拦截器: 拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加 ...

最新文章

  1. es对分组后结果进行统计_ElasticSearch里面如何分组后根据sum值排序
  2. 人工智能的炒作_解密人工智能:是炒作还是我们期望太高
  3. php验证时区是否存在,php – 验证来自不同网站的时区名称?
  4. 正则基础学习1 这俩天补上正则全部内容,让你简单的学会正则,欢迎收藏当文档
  5. python中beautifulsoup_面向新手解析python Beautiful Soup基本用法
  6. PWN-PRACTICE-BUUCTF-10
  7. 程序员谈网络改变我们的生活
  8. android 生命周期流程图,Android Studio ——Service的生命周期
  9. 贝叶斯数据分析_科研进阶项目 | 剑桥大学 | 心理学、社会学、生物医学:统计数据分析(6.13开课)...
  10. 电脑黑屏的原因有哪些
  11. android 线程池 阻塞队列,【Android框架进阶〖02〗】ThinkAndroid线程池机制
  12. CPU+GPU异构集群搭建的总结说明
  13. TP6使用redis
  14. 猴子摘香蕉问题python_用Basic STRIPS算法求解猴子和香蕉
  15. background背景图片铺满背景并且不重复
  16. 转载了别人的cocos2d-x的学习笔记
  17. pca图解读_PCA 图像识别 详解(一)
  18. 解决第三方平台超时问题
  19. 数学物理方法·基础④复平面/辐角/复数表示形式
  20. CentOS7下安装Hadoop伪分布式

热门文章

  1. Linux系统下使用docker来部署Redis集群
  2. csh/tcsh脚本札记(持续更新:2021-8-24)
  3. pcap判断是否oracle,pcap的使用之pcap_findalldevs
  4. 证件照制作v2.9.38
  5. Beta冲刺(7/7)——2019.5.28
  6. Ubuntu/centos网络接口没有eth0但是有eth1或eth2的问题
  7. c#(服务器)与java(客户端)通过socket传递对象_C#(服務器)與Java(客戶端)通過Socket傳遞對象...
  8. 数据分析---常见分类算法
  9. 【Web技术】981- 手摸手之前端覆盖率实践
  10. 我的高效编程的秘诀:自动化你的编程工作