fix(client): 修复启动时 token 刷新导致 Dio adapter closed 崩溃
apiClientProvider 改用 select(isLoggedIn),token refresh 不触发 provider 重建,避免旧 Dio 被 close 后仍执行 fetch。同时在拦截器内原地更新 _dio.options.headers,保证后续请求使用新 token。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,25 +14,21 @@ final _publicDio = Dio(BaseOptions(
|
|||||||
));
|
));
|
||||||
|
|
||||||
final apiClientProvider = Provider<ApiClient>((ref) {
|
final apiClientProvider = Provider<ApiClient>((ref) {
|
||||||
final authState = ref.watch(authStateProvider);
|
// 只监听登录/登出,不监听 token 内容变化。
|
||||||
final myToken = authState.user?.accessToken;
|
// 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(
|
final client = ApiClient(
|
||||||
token: myToken,
|
token: user?.accessToken,
|
||||||
refreshToken: authState.user?.refreshToken,
|
refreshToken: user?.refreshToken,
|
||||||
onTokenRefreshed: (newToken) {
|
onTokenRefreshed: (newToken) {
|
||||||
debugPrint('[ApiClient] token refreshed');
|
ref.read(authStateProvider.notifier).updateAccessToken(newToken);
|
||||||
if (ref.read(authStateProvider).user?.accessToken == myToken) {
|
|
||||||
ref.read(authStateProvider.notifier).updateAccessToken(newToken);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onAuthFailed: () {
|
onAuthFailed: () {
|
||||||
debugPrint('[ApiClient] onAuthFailed! myToken prefix: ${(myToken ?? '').substring(0, (myToken ?? '').length.clamp(0, 20))}');
|
if (ref.read(authStateProvider).isLoggedIn) {
|
||||||
if (ref.read(authStateProvider).user?.accessToken == myToken) {
|
|
||||||
debugPrint('[ApiClient] calling logout()');
|
|
||||||
ref.read(authStateProvider.notifier).logout();
|
ref.read(authStateProvider.notifier).logout();
|
||||||
} else {
|
|
||||||
debugPrint('[ApiClient] skipping logout (stale client)');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -77,8 +73,10 @@ class ApiClient {
|
|||||||
});
|
});
|
||||||
final newToken =
|
final newToken =
|
||||||
resp.data['data']['access_token'] as String;
|
resp.data['data']['access_token'] as String;
|
||||||
|
// 原地更新 Dio 默认 headers,后续所有请求生效
|
||||||
|
_dio.options.headers['Authorization'] = 'Bearer $newToken';
|
||||||
if (!_disposed) onTokenRefreshed?.call(newToken);
|
if (!_disposed) onTokenRefreshed?.call(newToken);
|
||||||
// 用新 token 重试原请求
|
// 用新 token 重试原请求(此时 _dio 仍存活,不会 adapter closed)
|
||||||
final opts = e.requestOptions;
|
final opts = e.requestOptions;
|
||||||
opts.headers['Authorization'] = 'Bearer $newToken';
|
opts.headers['Authorization'] = 'Bearer $newToken';
|
||||||
final retryResp = await _dio.fetch(opts);
|
final retryResp = await _dio.fetch(opts);
|
||||||
|
|||||||
Reference in New Issue
Block a user