-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
QueuedInterceptors does not work with parallel requests #2342
Comments
生气,然后呢,有事说事就可以了,还得照顾你的情绪呗,开源变成情绪大乱斗了? @seunghwanly Do you have insights about making parallel requests work with the queued interceptor? The provided code is based on the example: https://github.com/cfug/dio/blob/main/example_dart/lib/queued_interceptor_crsftoken.dart |
I'll come up with some parallel examples soon. |
Cool! Thanks for the update. |
The Code sampleimport 'dart:convert';
import 'dart:math';
import 'package:dio/dio.dart';
/// Pretend as Authentication Server that generates access token and refresh token
class AuthenticationServer {
static Map<String, String> generate() => <String, String>{
'access_token': _generateUuid(),
'refresh_token': _generateUuid(),
};
static String _generateUuid() {
final random = Random.secure();
final bytes = List<int>.generate(8, (_) => random.nextInt(256));
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
}
}
typedef TokenHistory = ({
String? previous,
String? current,
DateTime updatedAt,
int updatedBy,
});
class TokenManager {
static String? _accessToken;
String? get accessToken => _accessToken;
static final List<TokenHistory> _history = <TokenHistory>[];
void setAccessToken(String? token, int instanceId) {
final previous = _accessToken;
_accessToken = token;
_history.add(
(
previous: previous,
current: _accessToken,
updatedAt: DateTime.now(),
updatedBy: instanceId,
),
);
}
void printHistory() {
print('=== Token History ===');
for (int i = 0; i < _history.length; i++) {
final entry = _history[i];
print('''
[$i]\tupdated token: ${entry.previous} → ${entry.current}
\tupdated at: ${entry.updatedAt.toIso8601String()}
\tupdated by: ${entry.updatedBy}
''');
}
}
}
void main() async {
final tokenManager = TokenManager();
final dio = Dio(
BaseOptions(
baseUrl: 'https://httpbun.com/',
),
);
dio.interceptors.add(
QueuedInterceptorsWrapper(
onRequest: (requestOptions, handler) {
final requestNumber = requestOptions.hashCode;
print(
'''
[onRequest] ${requestOptions.hashCode} / time: ${DateTime.now().toIso8601String()}
\tPath: ${requestOptions.path}
\tHeaders: ${requestOptions.headers}
''',
);
if (tokenManager.accessToken != null) {
requestOptions.headers['Authorization'] =
'Bearer ${tokenManager.accessToken}';
}
return handler.next(requestOptions);
},
onResponse: (response, handler) {
print('''
[onResponse] ${response.requestOptions.hashCode} / time: ${DateTime.now().toIso8601String()}
\tStatus: ${response.statusCode}
\tData: ${response.data}
''');
return handler.resolve(response);
},
onError: (error, handler) async {
final statusCode = error.response?.statusCode;
print(
'''
[onError] ${error.requestOptions.hashCode} / time: ${DateTime.now().toIso8601String()}
\tStatus: $statusCode
''',
);
/// Only handles 401 in this example
if (statusCode != 401) {
return handler.resolve(error.response!);
}
final tokenRefreshDio = Dio()..options.baseUrl = 'https://httpbun.com/';
final response = await tokenRefreshDio.post(
'https://httpbun.com/mix/s=201/b64=${base64.encode(jsonEncode(AuthenticationServer.generate()).codeUnits)}',
);
if (response.statusCode == null || response.statusCode! ~/ 100 != 2) {
return handler.reject(error);
}
final body = jsonDecode(response.data) as Map<String, Object?>;
if (!body.containsKey('access_token')) {
return handler.reject(error);
}
final token = body['access_token'] as String;
tokenManager.setAccessToken(token, error.requestOptions.hashCode);
tokenRefreshDio.close();
/// Pretend authorization has been resolved and try again
final retried = await dio.fetch(
error.requestOptions
..path = '/mix/s=200'
..headers = {
'Authorization': 'Bearer ${tokenManager.accessToken}',
},
);
if (retried.statusCode == null || retried.statusCode! ~/ 100 != 2) {
return handler.reject(error);
}
return handler.resolve(error.response!);
},
),
);
await Future.wait([
dio.post('/mix/s=401'),
dio.post('/mix/s=401'),
dio.post('/mix/s=200'),
]);
tokenManager.printHistory();
}
Console(output)[onRequest] 388747248 / time: 2024-12-20T01:26:04.617883
Path: /mix/s=401
Headers: {}
[onRequest] 731513614 / time: 2024-12-20T01:26:04.619478
Path: /mix/s=401
Headers: {}
[onRequest] 1229983 / time: 2024-12-20T01:26:04.619514
Path: /mix/s=200
Headers: {}
[onResponse] 1229983 / time: 2024-12-20T01:26:05.099799
Status: 200
Data:
[onError] 731513614 / time: 2024-12-20T01:26:05.102982
Status: 401
[onRequest] 731513614 / time: 2024-12-20T01:26:05.549759
Path: /mix/s=200
Headers: {Authorization: Bearer 9f0ef79368ea8b47}
[onResponse] 731513614 / time: 2024-12-20T01:26:05.690177
Status: 200
Data:
[onError] 388747248 / time: 2024-12-20T01:26:05.690450
Status: 401
[onRequest] 388747248 / time: 2024-12-20T01:26:06.114056
Path: /mix/s=200
Headers: {Authorization: Bearer e2d04742db8f8954}
[onResponse] 388747248 / time: 2024-12-20T01:26:06.255467
Status: 200
Data:
=== Token History ===
[0] updated token: null → 9f0ef79368ea8b47
updated at: 2024-12-20T01:26:05.546387
updated by: 731513614
[1] updated token: 9f0ef79368ea8b47 → e2d04742db8f8954
updated at: 2024-12-20T01:26:06.113834
updated by: 388747248
Process finished with exit code 0 |
牛儿逼之。tanks!!! |
Result:
We should consider more complex concurrency scenarios, and check if the token has changed before and after the refresh. |
@seunghwanly I made a small modification to the onError callback. Code:
Test:
Result:
|
@Passer-by Thank you for providing such a detailed example! Comparing the current token with the saved one can indeed help reduce the cost associated with token updates. 👍 |
@seunghwanly Are you interested in updating this example? queued_interceptor_crsftoken |
Sure I'll update and create a pull request this week! Thanks for your support :) |
牛而逼之。解决了并发状态下,频繁刷新token的问题。I can't express my gratitude!!! |
Package
dio
Version
5.7.0
Operating-System
Android, iOS, Web, MacOS, Linux, Windows
Adapter
Default Dio
Output of
flutter doctor -v
No response
Dart Version
No response
Steps to Reproduce
Expected Result
第一个请求刷新令牌后,第二个请求可以直接使用
Actual Result
暂无.恶心的 QueuedInterceptorsWrapper无效果,跟文档说明的不一样
The text was updated successfully, but these errors were encountered: