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; } } }
关键改动说明
- 包装请求流程:通过
_handleRequest方法封装完整的请求、错误处理、重发逻辑,再用ResponseFuture包裹返回,确保业务层拿到的是最终处理结果。 - 并发刷新控制:添加
_refreshLock静态变量,避免多个请求同时触发令牌过期时重复执行刷新操作。 - 简化调用逻辑:直接使用传入的
invoker发起请求,无需依赖super.interceptUnary的返回值。
业务层代码无需修改
你的loadUser函数保持原样即可,现在当令牌过期时,拦截器会自动完成刷新并重发请求,try块会获取到重发后的正确响应,不会进入catch块。
内容的提问来源于stack exchange,提问作者skyface753
相关产品推荐
相关产品推荐

