dart FlutterDio拦截器错误:错误状态:将来已完成

oipij1gg  于 2022-12-15  发布在  Flutter
关注(0)|答案(4)|浏览(522)

我有一个拦截器来发送jwt token,并在jwt过期时使用refresh_token端点。

Error: Bad state: Future already completed

错误,但请求还是被正确处理了。在控制台中,我看到一个成功的响应和一个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);
  }
}

dio_配置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安全存储.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安全存储.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;
    }
}

移动的安全存储.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);
        }
    }
}

编辑:
问题出在第一个return super.onError(err, handler);中必须是return null;

yhived7q

yhived7q1#

您正在使用Dio来处理请求。Dio4.0.6版本(截至目前的最新版本)存在此已知问题。请参考GitHub here上的相同内容。
溶液
将您的Dio软件包降级到已知不存在此问题的最新稳定版本,直到发布新版本。
在您的pubspec.yaml.

dio: 4.0.4

然后再去拿包裹。

> flutter pub get
ffscu2ro

ffscu2ro2#

对于其他任何人有这个问题,它不是解决只降级dio:将dio降级到4.0.4,并从您的BaseOptions中删除connectTimeout

lztngnrs

lztngnrs3#

class InterceptorsWrapper extends QueuedInterceptorsWrapper {
@override
void onRequest(RequestOptions options,RequestInterceptorHandler handler){
 log('send request:${options.baseUrl}${options.path}');

 final accessToken = Storage.instance.box.read("accessToken");

 options.headers['Authorization'] = 'Bearer $accessToken';

 super.onRequest(options, handler);
}

@override
void onError(DioError err, ErrorInterceptorHandler handler) {
switch (err.type) {
  case DioErrorType.connectTimeout:
  case DioErrorType.sendTimeout:
  case DioErrorType.receiveTimeout:
    throw DeadlineExceededException(err.requestOptions);
  case DioErrorType.response:
    switch (err.response?.statusCode) {
      case 400:
        throw BadRequestException(err.requestOptions);
      case 401:
        throw UnauthorizedException(err.requestOptions);
      case 404:
        throw NotFoundException(err.requestOptions);
      case 409:
        throw ConflictException(err.requestOptions);
      case 500:
        throw InternalServerErrorException(err.requestOptions);
    }
    break;
  case DioErrorType.cancel:
    break;
  case DioErrorType.other:
    throw NoInternetConnectionException(err.requestOptions);
}
super.onError(err, handler);
}
}
...
...

这就是我如何完成我的Dio拦截器的,你不必在你的void onRequest()中返回任何东西,只需调用super.onRequest(),并且不要在拦截器类中使用handler示例

return handler.resolve(cloneReq);

那部分已经在onRequest()中完成了。2我用这种方法解决了我的问题,你也可以试试。
谢谢您。

o2rvlv0m

o2rvlv0m4#

相关问题:https://github.com/flutterchina/dio/issues/1480

有几个开放PR(尝试)解决此漏洞:

如果您不想像其他答案所建议的那样降级到dio 4.0.4,那么您可以依赖这些分支中的一些,直到其中一个分支合并到官方存储库中。
在我的例子中,我已经检查并测试了@ipcjs的解决方案,看起来工作正常:

dio:
  git:
    url: https://github.com/ipcjs/dio
    path: dio/
    ref: b77af132442bf3266ccf11b50ce909711455db3a

相关问题