-
-
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
Unexpected Behavior with ErrorInterceptor Not Catching DioException on Retried Requests #2120
Comments
+1 any update one this ? |
You are reusing the same |
You mean I have to use 3 different |
The example does not contain retries, so 2 instances were used for the flow. More instances do not mean more resource costs because it just acts as a manager. Better start to use packages like |
Isn't that retry? final originResult = await dio.fetch(options..path += '&pass=true');. |
cc @seunghwanly to provide a detailed explanation. |
In my example, I've used 2 different instances for
dio/example/lib/queued_interceptor_crsftoken.dart Lines 52 to 60 in ccc7666
on then when its token( And for the result, I just added handler when it has succeeded (not for error) like below. dio/example/lib/queued_interceptor_crsftoken.dart Lines 68 to 71 in ccc7666
@ykaito21's example at the top, using same instance might cause infinite loop and keep calling this cycle. onRequest is called
onError is called check on debug mode (look for |
@AlexV525 Umm .. should I provide more clearer example than the current one? for clear understanding |
Yeah feel free to request changes and then we can discuss |
@seunghwanly Thank you for your help. For me, your example is clear enough, and I understand the flow, but my point is what happen if
dio/example/lib/queued_interceptor_crsftoken.dart Lines 67 to 71 in ccc7666
indeed, it is the same instance |
If we need updated data from the server, I'd rather use another instance for the request, and for the same configuration I made an example like the one below.
Or else I might not use a different instance to request a retrial. In that case things like changing the host from cached to its origin. const originHost = 'origin.com';
const cachedHost = 'cached.origin.com';
final originDio = Dio();
... // [originDio]'s interceptor
onError: (error, handler) async {
/// Pretend we have an extension called [isTimeout]
/// returns `true` when the request past 2 min.
if (error.isTimeout) {
/// Set new host
dio.options.baseUrl = 'https://$cachedHost';
final result = await dio.fetch(dio.options);
/// handle result
// TODO
}
}
This depends on your code style. I sometimes use the I hope my answer was helpful enough. Please let me know if you need any further assistance. :) If we use the same instance in the P.S. I will soon request changes for the csrfToken example. |
I have a token refresh interceptor and now that I read this, I think I am actually seeing a similar behavior. |
You mean if I use the same instance in the
but if I use class ErrorInterceptor extends QueuedInterceptor {
final Dio dio;
final Dio dio2;
ErrorInterceptor(this.dio, this.dio2);
@override
Future<void> onError(
DioException err, ErrorInterceptorHandler handler) async {
print('onError is called');
try {
// Without try-catch, it will throw unhandled exception
await dio2.fetch(err.requestOptions);
} catch (e) {
print('onError is called again');
}
handler.next(err);
}
}
void main() async {
var dio = Dio();
var dio2 = Dio();
// Add the custom interceptor
dio.interceptors.addAll([
ErrorInterceptor(dio, dio2),
]);
// Making a GET request
try {
print('Making a GET request...');
Response response =
await dio.get('https://example.com/this-does-not-exist');
print(response.data);
} catch (e) {
print("Final error: $e");
}
} Really appreciate your assistance! |
Yes, it create a cycle like this.
the actual outputs is just like as what you added as
Yes, Control with handler's method
|
I updated example/lib/queued_interceptor_crsftoken.dart here. It seems to be more clear than before like handling errors and queued interceptors. |
@seunghwanly, thanks for the detailed explanation and updated example. I have a question about your example, I don't see a case in onError for an automatic retry with a new token, if I wanted to add that, could I just create a new dio in onError and use that like tokenDio? /// Add `onError` interceptor to request new CSRF token
dio.interceptors.add(
QueuedInterceptorsWrapper(
/// Request new CSRF token
/// if the response status code is `401`
onError: (error, handler) async {
log('Error Detected: ${error.message}');
if (error.response == null) return handler.next(error);
if (error.response?.statusCode == 401) {
try {
final tokenDio = Dio(
BaseOptions(baseUrl: error.requestOptions.baseUrl),
);
/// Generate CSRF token
///
/// This is a MOCK REQUEST to generate a CSRF token.
/// In a real-world scenario, this should be generated by the server.
final result = await tokenDio.post(
'/response-headers',
queryParameters: {
_headerKey: '94d6d1ca-fa06-468f-a25c-2f769d04c26c',
},
);
if (result.statusCode == null || result.statusCode! ~/ 100 != 2) {
throw DioException(requestOptions: result.requestOptions);
}
final updatedToken = result.headers.value(_headerKey);
if (updatedToken == null) throw ArgumentError.notNull(_headerKey);
cachedCSRFToken = updatedToken;
// Can I do like this? or is there better way?
final retryDio = Dio(
BaseOptions(baseUrl: error.requestOptions.baseUrl),
);
final requestOptions = err.requestOptions;
requestOptions.headers['token'] = '$updatedToken';
final res = retryDio.fetch(requestOptions);
return handler.resolve(res);
} on DioException catch (e) {
return handler.reject(e);
}
}
},
),
); |
Yes, I wrote the example for
use another dio to manage retry or other requests to handle exception cases. Here is an example for a retry interceptor. |
@seunghwanly Hi sorry for missing the thread. Could you submit the pull request? |
Thanks, I opened #2128 |
Package
dio
Version
5.4.1
Operating-System
iOS
Output of
flutter doctor -v
Dart Version
3.3.0
Steps to Reproduce
Description:
I am encountering an issue where my custom
ErrorInterceptor
retries a request usingdio.fetch
within itsonError
method when the initial request fails. Although I am explicitly catching exceptions within theonError
method, subsequent failures of the retried request do not seem to be caught by thetry-catch
block. Instead, theonRequest
interceptor is triggered again, but any subsequentDioException
thrown by the retried request does not seem to be caught within the sameonError
method.Steps to Reproduce:
RequestInterceptor
,ResponseInterceptor
, andErrorInterceptor
.ErrorInterceptor.onError
, retry the request usingawait dio.fetch(err.requestOptions);
and wrap this call in atry-catch
block to catch any exceptions.onError
catches the exception from the retried request.Code Snippet:
Additional Context:
I've tried various approaches to ensure that asynchronous operations are correctly awaited and that the
try-catch
block is properly structured to catch asynchronous exceptions, but the issue persists. This behavior suggests that there might be a problem with howDioException
is propagated or caught withinonError
when retrying requests.I am looking for guidance on whether this is expected behavior with the current Dio version or if there might be a bug affecting error handling in custom interceptors when retrying requests. Any insights or suggestions on how to ensure that exceptions from retried requests can be caught and handled within
onError
would be greatly appreciated.Expected Result
After the request fails and triggers
onError
, the retried request withinonError
should also throw aDioException
if it fails, which should then be caught by thetry-catch
block within the sameonError
method. This should allow for custom error handling or logging of the retried request failure.Expected Output:
Actual Result
The retried request within
onError
triggers theonRequest
interceptor again as expected, but if the retried request fails, thetry-catch
block withinonError
does not catch the subsequentDioException
. This behavior prevents custom error handling for the retried request failure from being executed as intended.** Actual Output:**
The text was updated successfully, but these errors were encountered: