基于ofetch的Nuxt API客户端实现401刷新Token后重试请求,但页面无法获取重试请求返回数据的问题
看起来你遇到的核心问题是重试请求的结果没正确传递回页面,同时刷新Token的请求还被自己的拦截器干扰了,我来帮你一步步梳理和修复:
首先拆解下你代码里的几个关键问题:
Refresh请求被拦截器“坑”了
你现在用modules.auth.refresh发起刷新请求,但这个方法是基于带拦截器的apiFetcher创建的——而apiFetcher的onRequest钩子会自动给所有请求加上Authorization: Bearer {旧的accessToken}头。但刷新Token的接口根本不需要旧的accessToken,反而这个无效的Token会被后端直接拒绝,触发401进入catch块跳转到登录页,导致你的重试逻辑根本没机会执行。重试请求的上下文传递不完整
就算refresh请求成功了,你直接替换options.headers的方式可能会丢失原请求的其他头信息,导致重试请求不符合后端要求。
接下来是具体的修复方案:
步骤1:绕开拦截器发起Refresh请求
不要用带钩子的apiFetcher发refresh请求,直接用原始的$fetch,避免自动添加无效的Authorization头:
async onResponseError({ request, response, options }) { if (response.status === 401) { try { // 直接用原始$fetch发起refresh请求,绕开我们的拦截器 const newTokens = await $fetch('/auth/refresh', { baseURL: nuxtApp.$config.public.apiBase, method: 'POST', body: { refresh_token: refreshTokenCookie.value } }) // 更新Cookie和状态管理里的Token accessTokenCookie.value = newTokens.access_token refreshTokenCookie.value = newTokens.refresh_token authStore.setAccessToken(newTokens.access_token) authStore.setRefreshToken(newTokens.refresh_token) // 合并原请求的headers,只更新Authorization options.headers = new Headers({ ...options.headers, 'Authorization': `Bearer ${newTokens.access_token}` }) // 重试原请求并返回结果,让最初的await能拿到重试后的响应 return apiFetcher(request, options) } catch (error) { // 注意路径要加斜杠,避免匹配错误 if (route.path !== '/auth') { navigateTo('/auth') } // 抛出错误,让原请求的调用链能捕获到问题 throw error } } }
步骤2:确保HttpFactory的调用逻辑正确
检查你的HttpFactory的call方法,确保它正确传递了请求选项,比如默认是GET请求,不会意外修改请求方法:
import type { FetchOptions } from 'ohmyfetch' class HttpFactory { constructor(private readonly $fetch: typeof $fetch) {} protected call(resource: string, options: FetchOptions = {}) { // 保留原请求的所有选项,只拼接资源路径 return this.$fetch(resource, options) } }
步骤3:给useAsyncData加错误排查(可选)
为了更清晰地定位问题,可以给页面的useAsyncData加上错误处理,看看请求是不是真的失败了:
const { data: categories, error } = useAsyncData('categories', async () => { return await $api.category.getAll() }) // 控制台打印错误,方便排查 if (error.value) { console.error('获取分类失败:', error.value) }
这样修改后,你的refresh请求不会被自己的拦截器干扰,能正常拿到新Token,重试请求的结果也会正确传递回useAsyncData,页面就能正常获取数据了。
备注:内容来源于stack exchange,提问作者Oleksandr

