Flutter Dio拦截器报错:Bad state: Future already completed 求助解决
解决Dio拦截器JWT刷新时的"Future already completed"错误
我实现了一个Dio拦截器,用于发送jwt token,并在JWT过期时调用refresh_token接口。当JWT过期时,遇到了如下错误:
Error: Bad state: Future already completed
请求仍能正常处理,但控制台中会先出现一个成功响应,随后出现一个401错误。
custom_interceptor.dart
class CustomInterceptor extends DefaultInterceptor { ISecureStorage secureStorageService = ISecureStorage(); @override void onRequest( RequestOptions options, RequestInterceptorHandler handler) async { LoginModel loginModel = await secureStorageService.readLoginModel(); options.headers = { "Content-type": "application/json", "Authorization": "Bearer ${loginModel.access_token}" }; return super.onRequest(options, handler); } @override void onError(err, handler) async { if (err.response?.statusCode == 401) { final Dio _dio = DioConfig().dio; LoginModel loginModel = await secureStorageService.readLoginModel(); Uri uri = Uri.https( "$BASE_URL", "/refresh_token_url"); try { await _dio.postUri(uri, data: { "refresh_token": loginModel.refresh_token, "grant_type": "refresh_token" }).then((value) async { if (value?.statusCode == 200) { await secureStorageService.deleteLoginModel(); LoginModel newLoginData = LoginModel.fromJson(value.data); await secureStorageService.saveLoginModel(loginModel: newLoginData); err.requestOptions.headers["Authorization"] = "Bearer " + newLoginData.refresh_token; final opts = new Options( method: err.requestOptions.method, headers: err.requestOptions.headers); final cloneReq = await _dio.request(err.requestOptions.path, options: opts, data: err.requestOptions.data, queryParameters: err.requestOptions.queryParameters); return handler.resolve(cloneReq); } return err; }); return super.onError(err, handler); } catch (e, st) { print("ERROR: " + e); print("STACK: " + st.toString()); return super.onError(err, handler); } } else { return super.onError(err, handler); } } } class DefaultInterceptor extends Interceptor { @override void onRequest( RequestOptions options, RequestInterceptorHandler handler) async { print( 'REQUEST[${options.method}] => PATH: ${options.path} | DATA => ${options.data} | JWT => ${options.headers}'); return super.onRequest(options, handler); } @override void onResponse(Response response, ResponseInterceptorHandler handler) { print( 'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path} | DATA => ${response.data}'); super.onResponse(response, handler); return; } @override void onError(DioError err, ErrorInterceptorHandler handler) async { print( 'ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path} | SENT_DATA => ${err.requestOptions.data} | RECEIVED_DATA => ${err.response?.data}'); return super.onError(err, handler); } }
dio_config.dart
class DioConfig { static DioConfig _singletonHttp; Dio _dio; get dio => _dio; factory DioConfig() { _singletonHttp ??= DioConfig._singleton(); return _singletonHttp; } DioConfig._singleton() { _dio = Dio(); } dispose() { _dio.close(); } }
i_secure_storage.dart
abstract class ISecureStorage { factory ISecureStorage() => getSecureStorage(); Future<LoginModel> readLoginModel() async => LoginModel.empty; Future<bool> saveLoginModel({LoginModel loginModel}) async => false; Future<bool> deleteLoginModel() async => false; }
web_secure_storage.dart
ISecureStorage getSecureStorage() => WebSecureStorageService(); class WebSecureStorageService implements ISecureStorage { final String _loginData = 'loginData'; html.Storage webStorage = html.window.localStorage; @override Future<LoginModel> readLoginModel() async { return webStorage[_loginData] == null ? LoginModel.empty : LoginModel.fromJson(jsonDecode(webStorage[_loginData])); } @override Future<bool> saveLoginModel({ LoginModel loginModel}) async { webStorage[_loginData] = jsonEncode(loginModel); return true; } @override Future<bool> deleteLoginModel() async { webStorage.remove(_loginData); return true; } }
mobile_secure_storage.dart
ISecureStorage getSecureStorage() => MobileSecureStorageService(); class MobileSecureStorageService implements ISecureStorage { final String _loginModel = 'loginModel'; FlutterSecureStorage storage = const FlutterSecureStorage(); @override Future<LoginModel> readLoginModel() async { try { dynamic _loginData = await storage.read(key: _loginModel); return _loginData == null ? LoginModel.empty : LoginModel.fromJson(jsonDecode(_loginData)); } on PlatformException catch (ex) { throw PlatformException(code: ex.code, message: ex.message); } } @override Future<bool> saveLoginModel({LoginModel loginModel}) async { try { await storage.write(key: _loginModel, value: jsonEncode(loginModel)); return true; } on PlatformException catch (ex) { throw PlatformException(code: ex.code, message: ex.message); } } @override Future<bool> deleteLoginModel() async { try { await storage.delete(key: _loginModel); return true; } on PlatformException catch (ex) { throw PlatformException(code: ex.code, message: ex.message); } } }
解决方案
问题出在onError方法中,当成功刷新token并重新发起请求后,代码仍然执行了return super.onError(err, handler);,导致handler被多次调用,触发了"Future already completed"错误。
将以下代码:
return super.onError(err, handler);
修改为:
return null;
修改后,当成功处理刷新逻辑并调用handler.resolve(cloneReq)后,不会再执行父类的错误处理逻辑,避免了重复完成Future的问题,控制台的错误和401响应也会消失。
内容的提问来源于stack exchange,提问作者Vi.
相关产品推荐
相关产品推荐

