You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中Post/Put请求刷新Token时陷入无限循环求助

问题:Next.js应用Token过期后Post请求陷入无限循环

在基于Next.js开发的应用中,当Token过期后发起Post请求会陷入无限循环:Post请求返回401,触发Refresh-Token操作返回200,但重新发起Post请求仍返回401,循环往复。

以下是处理Refresh-Token的api.ts代码:

import { GetServerSidePropsContext } from "next"
import axios, { AxiosError } from "axios"
import { setCookie, parseCookies, destroyCookie } from "nookies"

import { error as notifyError } from "helpers/notify/error"
import { STORAGE } from "constants/storage"
import { logOut } from "helpers/auth/logOut"
import { ErrorResponse } from "shared/types"

let isRefreshing = false
let failedRequestsQueue: {
  onSuccess(token: string): void
  onFailure(err: AxiosError): void
}[] = []

export function baseInstance(
  ctx: GetServerSidePropsContext | undefined = undefined
) {
  const cookies = parseCookies(ctx)

  const token = cookies[STORAGE.TOKEN_KEY]
  const api = axios.create({
    baseURL: process.env.NEXT_PUBLIC_API_URL
  })
  api.defaults.headers.common.Authorization = `Bearer ${token}`

  api.interceptors.response.use(
    response => response,
    (error: AxiosError) => {
      const expectedError =
        error.response &&
        error.response.status >= 400 &&
        error.response.status < 500

      if (!expectedError) {
        notifyError('Encontramos um problema por aqui.')
      }

      return Promise.reject(error)
    }
  )

  return api
}

export function setupAPIClient(
  ctx: GetServerSidePropsContext | undefined = undefined
) {
  const cookies = parseCookies(ctx)

  const token = cookies[STORAGE.TOKEN_KEY]

  const api = axios.create({
    baseURL: process.env.NEXT_PUBLIC_API_URL
  })

  api.defaults.headers.common.Authorization = `Bearer ${token}`
  axios.interceptors.request.use(
    config => {
      const cookies = parseCookies(ctx)

      const token = cookies[STORAGE.TOKEN_KEY]
      // eslint-disable-next-line no-param-reassign
      config.headers.authorization = `Bearer ${token}`
      api.defaults.headers.common.Authorization = `Bearer ${token}`
      return config
    },
    error => {
      Promise.reject(error)
    }
  )

  api.interceptors.response.use(
    response => response,
    (error: AxiosError<ErrorResponse>) => {
      const expectedError =
        error.response &&
        error.response.status >= 400 &&
        error.response.status < 500

      if (!expectedError && error.response.status !== 401) {
        notifyError('Encontramos um problema por aqui.')
      }

      if (error.response.status === 401) {
        if (error.response.data.message === 'Refresh token not found') {
          logOut()
        }
        const cookies = parseCookies(ctx)

        const { '@mf_rt_key': refresh_token } = cookies
        const originalConfig = error.config

        if (!isRefreshing) {
          isRefreshing = true

          api
            .put('/refresh-tokens', {
              refresh_token
            })
            .then(response => {
              const { token, refresh_token } = response.data

              setCookie(ctx, STORAGE.TOKEN_KEY, token, {
                maxAge: 30 * 24 * 60 * 60, // 30 days
                path: '/'
              })

              setCookie(ctx, STORAGE.REFRESH_TOKEN_KEY, refresh_token, {
                maxAge: 30 * 24 * 60 * 60, // 30 days
                path: '/'
              })

              api.defaults.headers.common.Authorization = `Bearer ${token}`

              failedRequestsQueue.forEach(request => request.onSuccess(token))
              failedRequestsQueue = []
            })
            .catch((error: AxiosError) => {
              failedRequestsQueue.forEach(request => request.onFailure(error))
              failedRequestsQueue = []

              if (process.browser) {
                logOut()
              }
            })
            .finally(() => {
              isRefreshing = false
            })
        }

        return new Promise((resolve, reject) => {
          failedRequestsQueue.push({
            onSuccess: (token: string) => {
              originalConfig.headers.Authorization = `Bearer ${token}`
              resolve(api(originalConfig))
            },
            onFailure: (error: AxiosError) => {
              reject(error)
            }
          })
        })
      }

      return Promise.reject(error)
    }
  )

  return api
}

我尝试了各种方法但均无效。

请求循环截图

内容的提问来源于stack exchange,提问作者Júnior Silva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 02:05:45