You need to enable JavaScript to run this app. 如何使用Riverpod正确处理身份验证令牌与GraphQL客户端
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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();
  }
}

原方案问题根因

  1. 全链路重建问题:原逻辑中令牌更新会触发GraphQL客户端、认证仓库、AuthNotifier全链路重建,正在执行的signIn方法持有旧的AuthNotifier引用,操作已销毁的实例就会抛出dispose异常。
  2. 认证头丢失问题:使用静态defaultHeaders只会在客户端初始化时读取一次令牌,客户端重建时机不对、旧实例未被释放时就会出现请求不带认证头的问题。

内容的提问来源于stack exchange,提问作者SalahAdDin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.18 09:06:03