我有一个拦截器来发送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;
4条答案
按热度按时间yhived7q1#
您正在使用
Dio
来处理请求。Dio
的4.0.6
版本(截至目前的最新版本)存在此已知问题。请参考GitHub here上的相同内容。溶液
将您的
Dio
软件包降级到已知不存在此问题的最新稳定版本,直到发布新版本。在您的pubspec.yaml.
然后再去拿包裹。
ffscu2ro2#
对于其他任何人有这个问题,它不是解决只降级dio:将dio降级到
4.0.4
,并从您的BaseOptions
中删除connectTimeout
。lztngnrs3#
这就是我如何完成我的Dio拦截器的,你不必在你的void onRequest()中返回任何东西,只需调用super.onRequest(),并且不要在拦截器类中使用handler示例
那部分已经在onRequest()中完成了。2我用这种方法解决了我的问题,你也可以试试。
谢谢您。
o2rvlv0m4#
相关问题:https://github.com/flutterchina/dio/issues/1480
有几个开放PR(尝试)解决此漏洞:
如果您不想像其他答案所建议的那样降级到
dio 4.0.4
,那么您可以依赖这些分支中的一些,直到其中一个分支合并到官方存储库中。在我的例子中,我已经检查并测试了@ipcjs的解决方案,看起来工作正常: