Skip to content

拦截器

拦截器是一种强大的机制,可用于拦截和修改 HTTP 请求与响应,与 Express.js 中的中间件非常相似。拦截器是在请求发送前和响应接收前执行的函数,适用于日志记录、修改请求头、修改响应等多种场景。

拦截器的基本用法如下:

js
// 添加请求拦截器
axios.interceptors.request.use(
  function (config) {
    // 在请求发送之前执行某些操作
    return config;
  },
  function (error) {
    // 处理请求错误
    return Promise.reject(error);
  }
);

// 添加响应拦截器
axios.interceptors.response.use(
  function (response) {
    // 状态码在 2xx 范围内的响应会触发此函数
    // 处理响应数据
    return response;
  },
  function (error) {
    // 状态码不在 2xx 范围内的响应会触发此函数
    // 处理响应错误
    return Promise.reject(error);
  }
);

移除拦截器

可以通过对要移除的拦截器调用 eject 方法来移除特定拦截器。也可以通过在 axios.interceptors 对象上调用 clear 方法来移除所有拦截器。以下是移除拦截器的示例:

js
// 移除请求拦截器
const myInterceptor = axios.interceptors.request.use(function () {
  /*...*/
});
axios.interceptors.request.eject(myInterceptor);

// 移除响应拦截器
const myInterceptor = axios.interceptors.response.use(function () {
  /*...*/
});
axios.interceptors.response.eject(myInterceptor);

以下是移除所有拦截器的示例:

js
const instance = axios.create();
instance.interceptors.request.use(function () {
  /*...*/
});
instance.interceptors.request.clear(); // 移除所有请求拦截器
instance.interceptors.response.use(function () {
  /*...*/
});
instance.interceptors.response.clear(); // 移除所有响应拦截器

拦截器的默认行为

添加请求拦截器时,默认被视为异步执行。当主线程被阻塞时(拦截器底层会创建一个 Promise,你的请求会被放到调用栈底部),这可能导致 axios 请求的执行出现延迟。如果你的请求拦截器是同步的,可以在选项对象中添加一个标志,告知 axios 同步运行该代码,从而避免请求执行的延迟。

js
axios.interceptors.request.use(
  function (config) {
    config.headers.test = "I am only a header!";
    return config;
  },
  null,
  { synchronous: true }
);

同步拦截器错误

同步请求拦截器抛出错误时,axios 会调用与该拦截器配对的 onRejected 处理器,并停止运行其余请求拦截器。如果处理器正常返回(包括返回 undefined 或已兑现的 Promise),该错误会被视为已处理,axios 将使用最后一个有效配置发送请求。处理器的返回值不会替换该配置。

若要阻止发送请求,请省略拒绝处理器,或让它抛出错误或返回已拒绝的 Promise。最终错误随后会继续传递给响应拒绝拦截器。

js
axios.interceptors.request.use(
  function validate(config) {
    if (!config.headers.has("Authorization")) {
      throw new Error("Authorization is required");
    }
    return config;
  },
  function rejectInvalidRequest(error) {
    return Promise.reject(error);
  },
  { synchronous: true }
);

仅记录错误的拒绝处理器可以正常返回,以保留现有的继续请求行为:

js
axios.interceptors.request.use(
  function prepare(config) {
    throw new Error("Optional preparation failed");
  },
  function logPreparationFailure(error) {
    console.warn(error);
    // 正常返回将使用最后一个有效配置发送请求。
  },
  { synchronous: true }
);

使用 runWhen 的拦截器

如果你希望根据运行时条件决定是否执行某个拦截器,可以在选项对象中添加 runWhen 函数。仅当 runWhen 返回 false 时,拦截器不会执行。该函数会以 config 对象作为参数调用(你也可以为其绑定自定义参数)。这对于只需在特定时机运行的异步请求拦截器非常实用。

js
function onGetCall(config) {
  return config.method === "get";
}
axios.interceptors.request.use(
  function (config) {
    config.headers.test = "special get headers";
    return config;
  },
  null,
  { runWhen: onGetCall }
);

拦截器执行顺序

请求拦截器与响应拦截器的执行顺序相反

请求拦截器按逆序执行(LIFO——后进先出)。最后添加的请求拦截器最先执行。

响应拦截器按添加顺序执行(FIFO——先进先出)。最先添加的响应拦截器最先执行。

下面的示例展示了三个请求拦截器和三个响应拦截器的完整执行顺序:

js
const instance = axios.create();

const interceptor = (id) => (base) => {
  console.log(id);
  return base;
};

instance.interceptors.request.use(interceptor("Request Interceptor 1"));
instance.interceptors.request.use(interceptor("Request Interceptor 2"));
instance.interceptors.request.use(interceptor("Request Interceptor 3"));
instance.interceptors.response.use(interceptor("Response Interceptor 1"));
instance.interceptors.response.use(interceptor("Response Interceptor 2"));
instance.interceptors.response.use(interceptor("Response Interceptor 3"));

// 控制台输出:
// Request Interceptor 3
// Request Interceptor 2
// Request Interceptor 1
// [发起 HTTP 请求]
// Response Interceptor 1
// Response Interceptor 2
// Response Interceptor 3

多个拦截器

你可以在同一个请求或响应上添加多个拦截器,同一拦截器链中的多个拦截器遵循以下规则:

  • 每个拦截器都会执行
  • 请求拦截器按照后进先出(LIFO)的顺序执行
  • 响应拦截器按照添加顺序(FIFO)执行
  • 只返回最后一个拦截器的结果
  • 每个拦截器接收其前驱拦截器的结果
  • 当成功回调中的拦截器抛出错误时
    • 后续的成功回调拦截器不会被调用
    • 后续的错误回调拦截器会被调用
    • 一旦被捕获,后续的成功回调拦截器将再次被调用(与 Promise 链的行为一致)

TIP

要深入了解拦截器的工作原理,可以查阅这里的测试用例。

axios is provided under MIT license