如何使用Riverpod正确处理身份验证令牌与GraphQL客户端
Riverpod 处理认证令牌与 GraphQL 客户端的正确实现方案
核心改造逻辑
从根源切断令牌更新后的全链路重建路径,同时实现请求时动态携带认证头,从根本上解决两个报错问题。
1. 重构 GraphQL 客户端实现
放弃令牌更新就重建客户端的逻辑,改用 GraphQL 官方提供的 AuthLink 动态获取最新令牌,客户端只会初始化一次,不会因为令牌变更触发重建:
// 令牌存储Provider保持不变 final StateProvider<String> tokenProvider = StateProvider<String>((_) => '', name: "Token Provider"); // 重构后的GraphQL客户端Provider final graphQLClientProvider = Provider<GraphQLClient>( (ref) { // 每次请求自动动态读取最新令牌注入请求头,无需重建客户端 final AuthLink authLink = AuthLink( getToken: () async { final token = ref.read(tokenProvider).state; return token.isNotEmpty ? 'Bearer $token' : null; }, ); // 拼接认证链路与请求链路 final Link link = authLink.concat( HttpLink('http://192.168.1.36:1337/graphql'), ); return GraphQLClient( cache: GraphQLCache(), link: link, ); }, name: "GraphQL Client Provider", );
该改造同时解决认证头丢失问题:每次发请求时都会读取最新的令牌注入头部,不会出现客户端更新不及时导致的认证头缺失。
2. 调整依赖注入逻辑
现在 GraphQL 客户端不会重建,对应的仓库、状态通知器都不会被触发重建,原有 Provider 仅需做微小调整即可:
final authRepositoryProvider = Provider<AuthRepository>( // 改用watch监听客户端,此处客户端不会变更,不会触发重建 (ref) => GraphQLAuthRepository(client: ref.watch(graphQLClientProvider)), name: 'Auth Repository Provider', ); // AuthNotifierProvider无需修改,不会再因为令牌更新被重建 final authNotifierProvider = StateNotifierProvider<AuthNotifier, AuthState>( (ref) => AuthNotifier( authRepository: ref.watch(authRepositoryProvider), dataStore: ref.watch(dataStoreRepositoryProvider), profileRepository: ref.watch(profileRepositoryProvider), tokenController: ref.watch(tokenProvider.notifier), userController: ref.watch(userEntityProvider.notifier), ), name: "Authentication Notifier Provider", );
3. 兼容异常场景的signIn方法优化
新增mounted判断覆盖极端场景下的实例销毁问题:
Future<String?> signIn({ required String username, required String password, }) async { try { final Map<String, dynamic> rawUser = await authRepository.signIn( identifier: username, password: password) as Map<String, dynamic>; final User sessionUser = User.fromJson(rawUser); if (sessionUser.confirmed != false) { tokenController.state = sessionUser.token!; } // 新增mounted判断,避免极端场景下实例销毁后操作报错 if (!mounted) return null; await dataStore.writeUserProfile(sessionUser); userController.state = sessionUser; state = const AuthState.loggedIn(); } on LogInFailureByBadRequest { return "E-posta veya şifre geçersiz."; } on LogInFailure catch (error) { return error.toString(); } }
原方案问题根因
- 全链路重建问题:原逻辑中令牌更新会触发GraphQL客户端、认证仓库、AuthNotifier全链路重建,正在执行的signIn方法持有旧的AuthNotifier引用,操作已销毁的实例就会抛出dispose异常。
- 认证头丢失问题:使用静态
defaultHeaders只会在客户端初始化时读取一次令牌,客户端重建时机不对、旧实例未被释放时就会出现请求不带认证头的问题。
内容的提问来源于stack exchange,提问作者SalahAdDin
相关产品推荐
相关产品推荐

