axios 拦截器在请求或响应被 then、catch 之前进行拦截处理。
axios 的三大特性:基于 Promise、同构(可以用于浏览器和 node)、拦截器。
基于 fetch 实现拦截器。
1. 拦截器
请求和响应拦截器的使用方式相同。因此,实现一个拦截器管理器类来生成拦截器实例。拦截器管理器的主要功能是对拦截器进行维护,并提供一个方法来获取所有注册的拦截器。
class InterceptorManager {
constructor() {
// 保存拦截器
this.handlers = []
}
use(fulfilled, rejected) {
this.handlers.push([fulfilled, rejected])
}
get() {
return this.handlers
}
}
2. 封装 fetch
fetch 是浏览器基于 Promise 实现的另一种异步获取网络资源的方法。由于不支持插件或其他扩展,所以需要封装一个请求方法,这样就可以实现拦截器功能了。方法名为 fetchPlus,向添加两个属性,requestInterceptors 用于 responseInterceptors 注册拦截器。
// 创建基于 fetch 的方法
function fetchPlus(url, config) {
return fetch(url, config)
}
// 设置请求拦截器
fetchPlus.requestInterceptors = new InterceptorManager()
// 设置响应拦截器
fetchPlus.responseInterceptors = new InterceptorManager()
3. 拦截请求
拦截器可能是异步的,所以不能直接遍历执行拦截器函数。对于异步函数,可以使用 Promise 链接调用。
function fetchPlus(url, config) {
const requestInterceptors = fetchPlus.requestInterceptors.get()
const responseInterceptors = fetchPlus.responseInterceptors.get()
const dispatchRequest = config => fetch(url, config)
const chain = [
...requestInterceptors,
[dispatchRequest, undefined],
...responseInterceptors
]
let promise = Promise.resolve(config)
while (chain.length) {
const [fulfilled, rejected] = chain.shift()
promise = promise.then(fulfilled, rejected)
}
return promise
}
fetchPlus.requestInterceptors = new InterceptorManager()
fetchPlus.responseInterceptors = new InterceptorManager()
4. 测试
首先模拟 dispatchRequest,修改 fetchPlus 中的 dispatchRequest:
const dispatchRequest = config => {
console.log('fetch start')
return new Promise(resolve => {
setTimeout(() => {
console.log('request done')
resolve({ code: 0, data: { name: 'jack' } })
}, 500)
})
}
设置拦截器:
// 请求拦截器
fetchPlus.requestInterceptors.use(config => {
console.log('request interceptor 1')
console.log(config)
return config
})
fetchPlus.requestInterceptors.use(config => {
console.log('请求拦截器 2')
if (config.method === 'post') {
config.body = JSON.stringify(config.data)
delete config.data
config.headers = config.headers || {}
config.headers['content-type'] = 'application/json'
}
return config
})
fetchPlus.requestInterceptors.use(config => {
console.log('request interceptor 3')
return config
})
// 响应拦截器
fetchPlus.responseInterceptors.use(res => {
if (!res.data) {
console.log('error')
throw new Error(res)
}
return res
})
最后用 fetchPlus 发送请求:
fetchPlus('http://examples.com', { method: 'post', data: { foo: 'bar' } })
.then(res => {
console.log('response->', res)
})
.catch(err => {
console.log('response error->', err)
})