From dc6e68154b09b1e9f2465c6d75563a1a531c09be Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Wed, 8 Apr 2026 08:34:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(client):=20=E4=BF=AE=E5=A4=8D=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E6=97=B6=20token=20=E5=88=B7=E6=96=B0=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=20Dio=20adapter=20closed=20=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apiClientProvider 改用 select(isLoggedIn),token refresh 不触发 provider 重建,避免旧 Dio 被 close 后仍执行 fetch。同时在拦截器内原地更新 _dio.options.headers,保证后续请求使用新 token。 Co-Authored-By: Claude Sonnet 4.6 --- client/lib/core/api/api_client.dart | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/client/lib/core/api/api_client.dart b/client/lib/core/api/api_client.dart index 154aaa0..cf666ee 100644 --- a/client/lib/core/api/api_client.dart +++ b/client/lib/core/api/api_client.dart @@ -14,25 +14,21 @@ final _publicDio = Dio(BaseOptions( )); final apiClientProvider = Provider((ref) { - final authState = ref.watch(authStateProvider); - final myToken = authState.user?.accessToken; + // 只监听登录/登出,不监听 token 内容变化。 + // token refresh 只改 accessToken,isLoggedIn 不变,provider 不重建, + // 避免旧 Dio 被 close 后还在执行 fetch 导致 "adapter was closed"。 + ref.watch(authStateProvider.select((s) => s.isLoggedIn)); + final user = ref.read(authStateProvider).user; final client = ApiClient( - token: myToken, - refreshToken: authState.user?.refreshToken, + token: user?.accessToken, + refreshToken: user?.refreshToken, onTokenRefreshed: (newToken) { - debugPrint('[ApiClient] token refreshed'); - if (ref.read(authStateProvider).user?.accessToken == myToken) { - ref.read(authStateProvider.notifier).updateAccessToken(newToken); - } + ref.read(authStateProvider.notifier).updateAccessToken(newToken); }, onAuthFailed: () { - debugPrint('[ApiClient] onAuthFailed! myToken prefix: ${(myToken ?? '').substring(0, (myToken ?? '').length.clamp(0, 20))}'); - if (ref.read(authStateProvider).user?.accessToken == myToken) { - debugPrint('[ApiClient] calling logout()'); + if (ref.read(authStateProvider).isLoggedIn) { ref.read(authStateProvider.notifier).logout(); - } else { - debugPrint('[ApiClient] skipping logout (stale client)'); } }, ); @@ -77,8 +73,10 @@ class ApiClient { }); final newToken = resp.data['data']['access_token'] as String; + // 原地更新 Dio 默认 headers,后续所有请求生效 + _dio.options.headers['Authorization'] = 'Bearer $newToken'; if (!_disposed) onTokenRefreshed?.call(newToken); - // 用新 token 重试原请求 + // 用新 token 重试原请求(此时 _dio 仍存活,不会 adapter closed) final opts = e.requestOptions; opts.headers['Authorization'] = 'Bearer $newToken'; final retryResp = await _dio.fetch(opts);