返回
0
分享
拉黑
举报
axios Response 拦截器的大坑
忆读官方

Axios Response 拦截器的坑:为何明明登录了却仍然 401?

在使用 axios 进行前后端交互时,response 拦截器是一个常用的工具,能够帮助我们在收到服务器响应后进行统一处理,比如自动刷新 token、格式化数据或者全局错误处理。然而,axiosresponse 拦截器有一个不易察觉的“坑”——拦截器的执行时机是 resolve 之后,这可能导致在业务逻辑已经认为登录成功的情况下,紧接着的请求却仍然返回 401,给开发者带来极大的困惑。

看似正确的 token 处理方式

很多开发者在处理 token 时,会采用类似下面的 axios 拦截器逻辑:

axios.interceptors.response.use(
  response => {
    if (response.data.token) {
      localStorage.setItem("token", response.data.token);
    }
    return response;
  },
  async error => {
    if (error.response.status === 401) {
      const newToken = await refreshToken();
      localStorage.setItem("token", newToken);
      return axios(error.config); // 重新发送原请求
    }
    return Promise.reject(error);
  }
);

表面上看,这段代码似乎没有问题:

  • 如果服务器返回了新的 token,就更新本地存储中的 token。
  • 如果遇到 401,就刷新 token,然后重新发起请求。

但是,当你真正运行代码后,可能会发现一个奇怪的现象:

明明已经登录成功了,为什么接下来的请求仍然是 401?

问题的本质:拦截器比 resolve 慢一步

拦截器的执行时机

axiosresponse 拦截器并不会立即生效,而是在 Promise.resolve(response) 之后才会执行。这意味着:

  1. 业务代码在 await axios.post("/login") 返回后,认为自己已经完成了登录,直接开始执行后续请求。
  2. 但是此时 token 可能还没有更新,因为 response 拦截器尚未执行。
  3. 于是,后续的请求仍然携带的是旧的、失效的 token,导致服务器返回 401。

示例:问题复现

假设我们的登录逻辑如下:

async function login() {
  const res = await axios.post("/login", { username, password });
  console.log("登录成功,token:", localStorage.getItem("token")); 
  fetchUserData();  // 立刻发起请求
}

function fetchUserData() {
  axios.get("/user/profile", {
    headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }
  }).then(res => {
    console.log("用户信息:", res.data);
  });
}

可能的执行顺序

  1. axios.post("/login") 发起请求,服务器返回 token。
  2. await axios.post("/login") 解析 response,业务代码继续执行。
  3. fetchUserData() 立刻执行,获取 token 并发送请求。
  4. 此时拦截器还未执行,token 仍然是旧的或 null,请求失败,返回 401。

这样,登录之后的第一个 API 请求可能失败,导致一些奇怪的 Bug,例如:

  • 登录后页面刷新失败(因为数据请求 401)。
  • 需要重新登录才能成功访问用户数据。

这个坑有多隐蔽?

这个问题极其隐蔽,原因如下:

  1. 时机上的细微差异:拦截器的执行虽然是异步的,但其延迟足以导致问题。
  2. 大多数情况下可能正常:如果 token 过期时间长,或者后续 API 请求稍微晚一点,问题就不会发生,因此它通常只在某些特定情况下触发。
  3. 无法直观看到 token 更新的时间点:即使 console.log(localStorage.getItem("token")) 也可能误导你,让你以为 token 早已更新。

误区:用 then() 处理拦截器

一些开发者可能会尝试用 .then() 确保 token 已更新:

axios.post("/login", { username, password }).then(() => {
  fetchUserData();
});

但这并不能解决问题,因为 then() 只是在 response 解析完之后执行,并不能保证拦截器已经完成 token 的更新。

如何正确认知这个坑?

核心点拦截器是在 resolve 之后执行的,而不是在 axios 响应返回时立即生效。

因此,开发者在使用 axios 拦截器处理 token 时,应该始终考虑:

  1. 后续请求是否可能比 token 更新更早发出?
  2. token 逻辑是否真正等待拦截器更新完成?

结语

Axios 的 response 拦截器提供了强大的功能,但它的执行时机往往容易被误解,导致在业务代码认为 token 已更新时,实际还未更新,最终造成 401 问题。理解这一点,可以帮助你更好地设计 token 更新逻辑,避免不必要的 Bug。




免责声明:本文来自部分自媒体或者个人作者,不代表忆读网的观点和立场。
发表评论文明上网理性发言
发表

评论列表(0条)

快来留下您的看法~