当前位置: 首页>后端>正文

实现像 axios 一样的请求拦截器

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)
  })

https://www.xamrdz.com/backend/3um1938326.html

相关文章: