(
url: string,
options: RequestInit = {}
)
| 218 | * 当遇到 401 错误时,自动尝试刷新 token 并重试请求 |
| 219 | */ |
| 220 | export async function fetchWithAuth<T>( |
| 221 | url: string, |
| 222 | options: RequestInit = {} |
| 223 | ): Promise<T> { |
| 224 | const token = typeof window !== 'undefined' ? localStorage.getItem('access_token') : null; |
| 225 | |
| 226 | const headers: HeadersInit = { |
| 227 | 'Content-Type': 'application/json', |
| 228 | ...options.headers, |
| 229 | ...(token && { 'Authorization': `Bearer ${token}` }) |
| 230 | }; |
| 231 | |
| 232 | const response = await fetch(url, { ...options, headers }); |
| 233 | |
| 234 | // 如果不是 401 或 403 错误,直接处理响应 |
| 235 | if (response.status !== 401 && response.status !== 403) { |
| 236 | return handleResponse<T>(response); |
| 237 | } |
| 238 | |
| 239 | // 处理 401/403 错误 - 尝试刷新 token |
| 240 | console.log(`[fetchWithAuth] 检测到 ${response.status} 错误,准备刷新 token...`); |
| 241 | // 注意:不需要检查 refresh_token 是否存在,因为它在 httpOnly cookie 中 |
| 242 | // 后端会自动从 cookie 读取 |
| 243 | |
| 244 | // 如果正在刷新,将请求加入队列 |
| 245 | console.log('[fetchWithAuth] 检查是否正在刷新:', isRefreshing); |
| 246 | if (isRefreshing) { |
| 247 | console.log('[fetchWithAuth] 正在刷新中,将请求加入队列'); |
| 248 | return new Promise<T>((resolve, reject) => { |
| 249 | failedQueue.push({ |
| 250 | resolve: async (newToken) => { |
| 251 | if (newToken) { |
| 252 | const retryHeaders: HeadersInit = { |
| 253 | ...headers, |
| 254 | 'Authorization': `Bearer ${newToken}` |
| 255 | }; |
| 256 | try { |
| 257 | const retryResponse = await fetch(url, { ...options, headers: retryHeaders }); |
| 258 | resolve(await handleResponse<T>(retryResponse)); |
| 259 | } catch (error) { |
| 260 | reject(error); |
| 261 | } |
| 262 | } else { |
| 263 | reject(new Error('Token refresh failed')); |
| 264 | } |
| 265 | }, |
| 266 | reject |
| 267 | }); |
| 268 | }); |
| 269 | } |
| 270 | |
| 271 | isRefreshing = true; |
| 272 | |
| 273 | try { |
| 274 | console.log('[fetchWithAuth] 正在调用刷新接口...'); |
| 275 | const refreshData = await refreshToken(); |
| 276 | console.log('[fetchWithAuth] Token 刷新成功'); |
| 277 |
no test coverage detected