Flutter Dio 拦截器错误:错误状态:Future 已完成

Vi.*_*Vi. 5 interceptor dart flutter dio

我有一个拦截器可以在 jwt 过期时发送jwt token和使用端点。refresh_token有了过期的 jwt 我得到

Error: Bad state: Future already completed
Run Code Online (Sandbox Code Playgroud)

错误,但请求仍然被正确处理。在控制台中,我看到一个成功的响应,另一个随后出现 401 错误。我该如何解决这个问题?

自定义拦截器.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);
  }
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

就我而言,问题出在第一个

return super.onError(err, handler);
Run Code Online (Sandbox Code Playgroud)

一定是return null;

所以我让它工作了

Ham*_*hid 6

您正在用于Dio请求。4.0.6截至目前的Dio最新版本存在此已知问题。请参阅 GitHub 上的相同内容

解决方案

将您的软件包降级Dio到最新的稳定版本,在新版本发布之前,该版本不会出现此问题。

在你的 pubspec.yaml 中。

dio: 4.0.4
Run Code Online (Sandbox Code Playgroud)

然后再去领取包裹。

> flutter pub get
Run Code Online (Sandbox Code Playgroud)