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

NUXT 3中使用useFetch组合式函数实现失败请求重试

Nuxt3 中实现 useFetch 自动刷新 Token 并重试请求

基于你已有的自定义useCustomFetch组合式,我们可以通过添加响应拦截逻辑,实现 Token 过期时自动刷新并重试原请求的功能,核心是处理401错误、避免并发重复刷新、重试请求这几个关键点。

实现步骤与代码示例

修改你的composables/useCustomFetch.ts,加入以下逻辑:

// composables/useCustomFetch.ts
import type { UseFetchOptions } from 'nuxt/app'
import { useAuthStore } from '~/stores/auth' // 假设用Pinia管理Auth状态
import { navigateTo } from 'nuxt/app'

// 全局锁:防止多个并发请求重复触发Token刷新
let isRefreshing = false
// 存储等待Token刷新后重试的请求队列
let refreshQueue: Array<() => void> = []

export function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
  const authStore = useAuthStore()
  const currentToken = authStore.getToken

  // 默认配置:自动携带Authorization头
  const defaultOptions: UseFetchOptions<T> = {
    headers: currentToken ? { Authorization: `Bearer ${currentToken}` } : {},
    // 响应错误拦截器
    onResponseError({ response, options }) {
      // 仅处理Token过期导致的401,排除刷新Token的请求本身(避免死循环)
      if (response.status === 401 && url !== '/api/auth/refresh') {
        // 如果正在刷新Token,将当前请求加入队列等待重试
        if (isRefreshing) {
          return new Promise(resolve => {
            refreshQueue.push(() => {
              // 更新请求头的Token
              options.headers = { ...options.headers, Authorization: `Bearer ${authStore.getToken}` }
              // 重试请求
              resolve(useCustomFetch(url, options))
            })
          })
        }

        isRefreshing = true
        // 发起Token刷新请求
        return useFetch('/api/auth/refresh', {
          method: 'POST',
          body: { refreshToken: authStore.getRefreshToken }
        }).then(async (refreshResult) => {
          if (!refreshResult.error.value) {
            const newToken = refreshResult.data.value?.accessToken
            const newRefreshToken = refreshResult.data.value?.refreshToken
            
            // 更新本地存储的Token
            authStore.updateToken(newToken, newRefreshToken)
            
            // 更新当前请求的Authorization头,并重试
            options.headers = { ...options.headers, Authorization: `Bearer ${newToken}` }
            const retryResult = await useCustomFetch(url, options)
            
            // 执行队列中所有等待的请求
            refreshQueue.forEach(cb => cb())
            refreshQueue = []
            
            return retryResult
          } else {
            // Token刷新失败,跳转登录页
            await navigateTo('/login')
            return Promise.reject(refreshResult.error.value)
          }
        }).finally(() => {
          // 释放刷新锁
          isRefreshing = false
        })
      }
    }
  }

  // 合并用户传入的配置与默认配置
  const mergedOptions = { ...defaultOptions, ...options }
  return useFetch(url, mergedOptions)
}

关键细节说明

  • 并发请求处理:通过isRefreshing锁和refreshQueue队列,确保多个请求同时遇到401时,只触发一次Token刷新,所有等待的请求在刷新完成后统一重试。
  • 避免死循环:判断当前请求不是刷新Token的接口,防止刷新接口本身返回401时无限重试。
  • 状态更新:刷新成功后,更新Auth状态中的Token,确保后续请求自动携带新Token。
  • 失败降级:如果Refresh Token也过期(刷新接口返回错误),直接跳转登录页,引导用户重新授权。

适配调整

  • 如果你的项目不用Pinia,可替换为useCookie存储Token和Refresh Token,比如:
    const token = useCookie('access_token')
    const refreshToken = useCookie('refresh_token')
    
  • 根据后端实际接口调整刷新Token的接口地址、请求方法和参数格式。
  • 部分后端会返回特定错误码标识Token过期,可在onResponseError中增加对响应体错误码的判断,而非仅依赖401状态码。

内容的提问来源于stack exchange,提问作者Praveen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 01:15:16