fix(api): replay requests after token refresh

This commit is contained in:
2026-07-02 12:08:56 +08:00
parent 5821a4d062
commit 51987882eb
4 changed files with 118 additions and 9 deletions
@@ -1,7 +1,7 @@
/**
* 认证刷新拦截器
*
* 处理 401 错误,自动刷新 token 并重试原请求
* 处理 401 错误,自动刷新 token;原请求重放由 ofetch retry 机制完成
*
*/
import type { FetchHook } from "ofetch";
@@ -132,7 +132,7 @@ function withTestAccountFlag<T extends Record<string, unknown>>(
}
/**
* 401 → 刷新 token 并重试
* 401 → 刷新 token,并允许 ofetch retry 重放原请求
*/
export const onResponseErrorAuthRefresh: FetchHook = async (ctx) => {
const status = ctx.response?.status;
@@ -144,10 +144,15 @@ export const onResponseErrorAuthRefresh: FetchHook = async (ctx) => {
const requestUrl =
ctx.request instanceof Request ? ctx.request.url : ctx.request;
const path = new URL(requestUrl).pathname;
if (NO_AUTH_REFRESH_PATHS.some((p) => path.includes(p))) return;
if (NO_AUTH_REFRESH_PATHS.some((p) => path.includes(p))) {
ctx.options.retry = false;
return;
}
// 已登录则刷新登录 token,否则刷新游客 token
const isGuestMode = !AuthStorage.getInstance().hasLoginToken();
const hasLoginTokenResult = await AuthStorage.getInstance().hasLoginToken();
const isGuestMode =
!Result.isOk(hasLoginTokenResult) || !hasLoginTokenResult.data;
// 并发刷新:等待或启动
if (isRefreshing && refreshPromise) {
@@ -177,10 +182,10 @@ export const onResponseErrorAuthRefresh: FetchHook = async (ctx) => {
);
}
// 刷新成功:更新请求头的 token
if (ctx.request instanceof Request) {
ctx.request.headers.set("Authorization", `Bearer ${newToken}`);
}
// 刷新成功:更新当前 optionsretry 时 tokenInterceptor 也会重新读取最新 token
const headers = new Headers(ctx.options.headers);
headers.set("Authorization", `Bearer ${newToken}`);
ctx.options.headers = headers;
};
/**
@@ -20,6 +20,7 @@ const NO_TOKEN_PATHS: readonly string[] = [
ApiPath.emailLogin,
ApiPath.register,
ApiPath.guestLogin,
ApiPath.refresh,
];
/**