【JavaScript】实现axios的拦截器

2023/02/10 10:31:50

axios 拦截器的配置方式

详细配置方式见这里:拦截器的配置方式

简单来说就是:用 use() 方法注册拦截器,在调用 request() 方法时调用拦截器。

axios.interceptors.request.use(
  (config) => {},
  (error) => {}
);
axios.interceptors.request.use(
  (config) => {},
  (error) => {}
);
axios.interceptors.response.use(
  (response) => {},
  (error) => {}
);
axios.get("https://www.xxx.com").then(
  (response) => {},
  (error) => {}
);

use() 方法的一参为处理逻辑,处理逻辑如果报错则执行二参的错误处理逻辑。

这里只考虑同步的执行方式,需要实现的功能包括:

  • 请求拦截器。
    • 请求拦截器的一参函数接收 config 对象,返回新的 config 对象,最终的 request() 方法用的是最后一个拦截器返回的 config 对象。
    • use() 一参函数报错就执行二参函数,接收 error 对象作为参数。
    • 设置多个请求拦截器时后设置的先执行。
  • 响应拦截器。
    • 响应拦截器的一参函数接收响应对象,返回新的响应对象,最终的 request() 方法返回最后一个响应拦截器返回的响应对象。
    • 响应拦截器总是在请求响应之后按照设置顺序执行。

模拟发送请求

先模拟实现一个简易的 axios:

function dispatchRequest(config) {
  if (config.error) {
    return Promise.reject(config.error);
  }

  const response = {
    result: true,
    data: config,
  };
  return Promise.resolve(response);
}

class Axios {
  request(url) {
    const config = {
      url,
    };
    return dispatchRequest(config);
  }
}
const axios = new Axios();

dispatchRequest() 方法为模拟发送请求方法,接收 config,返回响应对象,为方便观察,响应对象的 data 为传入的 config。

如果 config.error 为 true 表示请求失败,request() 返回 error 对象,否则表示请求成功,返回响应对象。

axios
  .request("https://www.xxx.com", {})
  .then((res) => {
    console.log("success", res);
  })
  .catch((error) => {
    console.log("error", error);
  });

注册拦截器

拦截器是通过 use() 方法注册的,这里模拟实现 use() 方法。

function use(onFulfilled, onRejected) {
  this.handlers.push(onFulfilled, onRejected);
}

class Axios {
  interceptors = {
    request: {
      handlers: [],
    },
    response: {
      handlers: [],
    },
  };

  constructor() {
    this.interceptors.request.use = this.interceptors.response.use = use;
  }
}

将传入 use() 的方法都 push 到 handlers 数组中。

触发拦截器

拦截器在 request() 方法中触发。

class Axios {
  interceptors = {
    request: {
      handlers: [],
    },
    response: {
      handlers: [],
    },
  };

  constructor() {
    this.interceptors.request.use = this.interceptors.response.use = use;
  }

  request(url, config) {
    const fullConfig = Object.assign(
      {
        url,
      },
      config
    );

    const chain = [dispatchRequest, null];
    // 请求拦截器
    const requestInterceptor = this.interceptors.request.handlers;
    let index = 0;
    let len = requestInterceptor.length;
    while (index < len) {
      chain.unshift(requestInterceptor[index++], requestInterceptor[index++]);
    }

    // 响应拦截器
    const responseInterceptor = this.interceptors.response.handlers;
    index = 0;
    len = responseInterceptor.length;
    while (index < len) {
      chain.push(responseInterceptor[index++], responseInterceptor[index++]);
    }

    // 合并为 Promise 链
    let promise = Promise.resolve(fullConfig);
    index = 0;
    len = chain.length;
    while (index < len) {
      promise = promise.then(chain[index++], chain[index++]);
    }

    return promise;
  }
}

完整代码

function dispatchRequest(config) {
  if (config.error) {
    return Promise.reject(config.error);
  }

  const response = {
    result: true,
    data: config,
  };
  return Promise.resolve(response);
}

function use(onFulfilled, onRejected) {
  this.handlers.push(onFulfilled, onRejected);
}

class Axios {
  interceptors = {
    request: {
      handlers: [],
    },
    response: {
      handlers: [],
    },
  };

  constructor() {
    this.interceptors.request.use = this.interceptors.response.use = use;
  }

  request(url, config) {
    const fullConfig = Object.assign(
      {
        url,
      },
      config
    );

    const chain = [dispatchRequest, null];
    // 请求拦截器
    const requestInterceptor = this.interceptors.request.handlers;
    let index = 0;
    let len = requestInterceptor.length;
    while (index < len) {
      chain.unshift(requestInterceptor[index++], requestInterceptor[index++]);
    }

    // 响应拦截器
    const responseInterceptor = this.interceptors.response.handlers;
    index = 0;
    len = responseInterceptor.length;
    while (index < len) {
      chain.push(responseInterceptor[index++], responseInterceptor[index++]);
    }

    // 合并为 Promise 链
    let promise = Promise.resolve(fullConfig);
    index = 0;
    len = chain.length;
    while (index < len) {
      promise = promise.then(chain[index++], chain[index++]);
    }

    return promise;
  }
}
const axios = new Axios();