🌘 实现拦截器
# 需求分析
要对请求和响应进行拦截。
实现如下的功能:
// 添加一个请求拦截器
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);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在
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
})
2
3
4
5
6
7
8
也可以支持删除某个拦截器,如下:
const myInterceptor = axios.interceptors.request.use(function () {/*...*/})
axios.interceptors.request.eject(myInterceptor)
2
# 整体设计
整个过程是一个链式调用的过程,而且每一个拦截器都可以支持同步和异步处理,自然想到使用 Promise 链的方式实现整个调用过程。
这个 Promise 链的执行过程中,请求拦截器 resolve
函数处理的是 config
对象,响应拦截器 resolve
函数处理的是 response
对象;
了解这个过程后,先要创建一个 拦截器管理类,允许添加、删除和遍历拦截器。
# 拦截器管理类实现
根据需求, axios
拥有一个 interceptors
对象属性,该属性又有 request
和 response
2 个属性,它们对外提供一个 use
方法来添加拦截器,可以把这两个属性看做是一个拦截器管理对象。
user
方法支持两个参数,对于 resolve
函数的参数,请求拦截器是 AxiosRequestConfig
类型的;对于 reject
函数的参数类型则是 any
类型的。
# 定义接口
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
}
2
3
4
5
6
7
8
9
10
11
12
13
定义
AxiosInterceptorManager
泛型接口,对于resolve
函数的参数,请求拦截器和响应拦截器是不同的。
修改 Aixos
的类型:
export interface Axios {
interceptors: {
request: AxiosInterceptorManager<AxiosRequestConfig>,
response: AxiosInterceptorManager<AxiosResponse>
}
// ...
}
2
3
4
5
6
7
8
# 代码实现
定义新的类 src/core/InterceptorManager
import { RejectedFn, ResolvedFn } 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
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
定义
InterceptorManager
泛型类,内部维护了一个私有属性interceptors
数组,存储拦截器。该类对外提供三个方法,其中use
接口添加拦截器到interceptors
,返回一个id
用于删除;forEach
接口遍历interceptors
,支持传入一个函数,遍历的过程会调用该函数,并把每一个interceptor
作为函数的参数传入;eject
接口删除拦截器,通过传入的拦截器id
删除。
# 链式调用实现
首先在 src/core/Axios
定义一个 interceptors
属性,类型如下接口:
interface Interceptors {
request: InterceptorManager<AxiosRequestConfig>
response: InterceptorManager<AxiosResponse>
}
2
3
4
Interceptors
类型有两个属性,一个请求拦截器管理类实例,另一个是响应拦截器管理类实例。在实例化Aixos
时,在它的构造器去初始化这个interceptors
实例属性。
然后修改 src/core/Axios
中 Axios
类中的 request
方法逻辑,添加拦截器链式调用的逻辑:
interface PromiseChain<T> {
resolved: ResolvedFn<T> | ((config: AxiosRequestConfig) => AxiosPromise)
rejected?: RejectedFn
}
2
3
4
定义
PromiseChain
接口。
request(url: any, config?: any): AxiosPromise {
if (typeof url === 'string') {
if (!config) {
config = {}
}
config.url = url
} else {
config = url
}
const chain: PromiseChain<any>[] = [
{
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
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
首先构造一个
PromiseChain
类型的数组chain
,并且把dispatchRequest
函数赋值给resolved
;接着先遍历请求拦截器插入到chain
的前面;然后再遍历响应拦截器插入到chain
的后面。接下来定义一个已经
resolve
的Promise
,循环这个chain
,拿到每个拦截器对象,把它们的resolved
函数和rejected
函数添加到promise.then
的参数中,这样就相当于通过Promise
的链式调用方式,实现了拦截器一层层的链式调用。要注意 拦截器的执行顺序。请求拦截器先执行后添加的,再执行先添加的;对于响应拦截器,先执行先添加的,后执行后添加的。
# 编写 DEMO 代码
examples/interceptor/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)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
运行后,可以查看到 请求头中添加了字段
test
,以及内容为321
,说明请求拦截器的执行顺序正确;最后控制台输出的内容为13
,说明 响应拦截器根据id
删除成功,并且执行顺序正确。拦截器是一个非常实用的功能,在实际的工作中可以利用它添加登录权限认证的功能等。