You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter gRPC拦截器处理过期AccessToken问题求助

解决Flutter gRPC拦截器自动刷新令牌并重发请求的问题

你的问题出在拦截器的错误处理逻辑上:直接在super.interceptUnary返回的ResponseFuture上调用catchError,只是给这个Future添加了一个错误回调,但原始的错误依然会顺着Future流传递到业务层的try/catch里,导致loadUser先捕获到错误。

正确的做法是重新包装整个请求流程,把错误捕获、令牌刷新、请求重发的逻辑整合为一个新的Future,让业务层拿到的是最终处理后的结果,而不是原始的错误。

修改后的AuthenticationInterceptor实现

class AuthenticationInterceptor extends ClientInterceptor {
  final storage = const FlutterSecureStorage();
  // 防止多个请求同时触发令牌刷新的并发锁
  static Future<void>? _refreshLock;

  FutureOr<void> _provider(Map<String, String> metadata, String uri) async {
    final accessToken = await storage.read(key: 'accessToken');
    metadata['Authorization'] = accessToken ?? "";
  }

  @override
  ResponseFuture<R> interceptUnary<Q, R>(
    ClientMethod<Q, R> method,
    Q request,
    CallOptions options,
    Invoker invoker,
  ) {
    // 包装请求逻辑,返回处理后的ResponseFuture
    return ResponseFuture(_handleRequest(method, request, options, invoker));
  }

  Future<R> _handleRequest<Q, R>(
    ClientMethod<Q, R> method,
    Q request,
    CallOptions options,
    Invoker invoker,
  ) async {
    try {
      // 第一次发起请求
      final response = await invoker(
        method,
        request,
        options.mergedWith(CallOptions(providers: [_provider])),
      );
      return response;
    } on GrpcError catch (e) {
      // 匹配令牌无效的错误
      if (e.code == StatusCode.invalidArgument && e.message == "Invalid auth token") {
        // 控制并发刷新,避免重复调用刷新接口
        if (_refreshLock == null) {
          _refreshLock = refreshTheToken();
        }
        await _refreshLock;
        _refreshLock = null; // 刷新完成释放锁

        // 刷新令牌后重新发起请求
        return invoker(
          method,
          request,
          options.mergedWith(CallOptions(providers: [_provider])),
        );
      }
      // 非令牌错误直接抛出
      rethrow;
    }
  }
}

关键改动说明

  1. 包装请求流程:通过_handleRequest方法封装完整的请求、错误处理、重发逻辑,再用ResponseFuture包裹返回,确保业务层拿到的是最终处理结果。
  2. 并发刷新控制:添加_refreshLock静态变量,避免多个请求同时触发令牌过期时重复执行刷新操作。
  3. 简化调用逻辑:直接使用传入的invoker发起请求,无需依赖super.interceptUnary的返回值。

业务层代码无需修改

你的loadUser函数保持原样即可,现在当令牌过期时,拦截器会自动完成刷新并重发请求,try块会获取到重发后的正确响应,不会进入catch块。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 21:15:21