axios 拦截器实现原理

Axios 是一个基于 Promise 的 HTTP 客户端,用于在浏览器和 node.js 中执行 HTTP 请求。它具备拦截请求和响应的能力,这使得开发者可以在请求被发送到服务器前或响应被传递给 then/catch 方法前,对其进行处理或修改。拦截器是 Axios 非常强大的特性之一,它们主要被用于日志记录、身份验证、如果请求失败时的重试机制等功能;允许你在请求发送到服务器之前或响应返回客户端之前对其进行修改或处理。
 
拦截器主要有两种:请求拦截器(request interceptors)和响应拦截器(response interceptors)。

请求拦截器

  • 请求拦截器在发送请求之前被调用。
  • 它可以修改请求的配置,如 headers、url、params 等。
  • 也可以在此阶段取消请求。
  • 请求拦截器的修改或添加的配置将被用于之后的请求发送。

响应拦截器

  • 响应拦截器在服务器的响应被 Axios 处理之前被调用。
  • 它可以修改响应数据,处理错误等。
  • 如果响应是一个正常的响应,可以直接返回数据或对数据进行修改。
  • 如果响应是一个错误(例如,404或500状态码),可以进行错误处理或重试逻辑。

实现原理

  1. 拦截器数组:
    Axios 内部维护了两个数组,一个用于存储请求拦截器,另一个用于存储响应拦截器。每个拦截器都是一个函数,这些函数按照它们在数组中定义的顺序被依次执行。

  2. 拦截器函数的参数:

    • 请求拦截器:通常接收一个配置对象(通常是请求的配置)作为参数,并返回一个配置对象或 Promise。
    • 响应拦截器:接收一个响应对象作为参数,并返回一个响应对象或 Promise。
  3. 拦截器的执行:

    • 当 Axios 发起一个请求时,它会首先遍历并执行请求拦截器数组中的每个函数。这些函数可以对请求进行预处理,比如添加请求头、处理错误等。
    • 一旦请求被发送并得到响应,Axios 会遍历并执行响应拦截器数组中的每个函数。这些函数可以对响应进行后处理,比如数据转换、错误处理等。
    • 当发出请求或接收响应时,Axios 会遍历这些拦截器,并按照添加的顺序执行请求拦截器,以及按照相反的顺序执行响应拦截器。
    • 在 Axios 的源码中,拦截器是通过一个 AxiosInterceptorManager 实例来管理的,它维护了一个拦截器数组。每个拦截器都是一个包含 fulfilledrejected 函数的对象。这两个函数分别对应于拦截器成功处理和拦截器处理出错的情况。
  4. Promise 链:
    由于拦截器函数可以返回 Promise,因此可以很容易地在拦截器中执行异步操作。Axios 会等待每个拦截器的 Promise 解决后再继续执行后续的拦截器或请求/响应处理。

  5. 取消拦截器:
    Axios 提供了取消拦截器的方法,允许你在不再需要某个拦截器时将其从数组中移除。

  6. 使用场景:

    • 身份验证或添加通用 headers:在请求拦截器中添加身份验证令牌(token)。
    • 性能监控:记录请求的延迟时间。
    • 错误处理:在响应拦截器中统一处理网络错误或服务器错误。
    • 数据转换:在响应拦截器中处理服务器返回的数据,比如解析 JSON 数据或进行其他格式转换。
    • 日志记录:在请求和响应拦截器中记录请求的详细信息,以便进行调试或监控。

示例代码

// 添加请求拦截器  
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 拦截器管理器的一个简化版本,展示了其核心实现思路:

class InterceptorManager {
  constructor() {
    this.handlers = []; // 存储拦截器的数组
  }

  use(fulfilled, rejected) {
    this.handlers.push({
      fulfilled: fulfilled,
      rejected: rejected
    });
    return this.handlers.length - 1; // 返回拦截器的ID
  }

  eject(id) {
    if (this.handlers[id]) {
      this.handlers[id] = null; // 移除拦截器
    }
  }

  forEach(fn) {
    this.handlers.forEach((h) => {
      if (h !== null) {
        fn(h);
      }
    });
  }
}
在发送请求或接收响应时,Axios 会创建一个 promise 链,并通过 forEach 方法将拦截器中的 fulfilledrejected 函数添加到这个链中。这样,每个拦截器都可以对请求或响应进行处理,然后将结果传递到链的下一个拦截器,或者在出错时结束链的执行。

注意事项

  • 拦截器是按顺序执行的,因此它们的顺序很重要。
  • 由于拦截器可以修改请求或响应数据,因此在使用它们时要特别小心,确保不要意外地修改了你不需要修改的数据。
  • 如果在拦截器中抛出了错误或返回了一个被拒绝的 Promise,那么后续的拦截器和请求/响应处理将不会被执行。
posted @ 2024-05-06 16:40  蓓蕾心晴  阅读(84)  评论(0编辑  收藏  举报