Skip to content
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

✨ Introduce DioExceptionReadableStringBuilder #2297

Merged
merged 11 commits into from
Jan 28, 2025
1 change: 1 addition & 0 deletions dio/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ See the [Migration Guide][] for the complete breaking changes list.**
- Fixes boundary inconsistency in `FormData.clone()`.
- Support `FileAccessMode` in `Dio.download` and `Dio.downloadUri` to change download file opening mode
- Fix `ListParam` equality by using the `DeepCollectionEquality`.
- Enables configuring the logging details of `DioException` globally and locally.

## 5.7.0

Expand Down
35 changes: 31 additions & 4 deletions dio/lib/src/dio_exception.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'options.dart';
import 'response.dart';
import 'utils.dart' show warningLog;

/// Deprecated in favor of [DioExceptionType] and will be removed in future major versions.
@Deprecated('Use DioExceptionType instead. This will be removed in 6.0.0')
Expand Down Expand Up @@ -205,6 +206,14 @@ class DioException implements Exception {
/// The error message that throws a [DioException].
final String? message;

/// Users can customize the content of [toString] when thrown.
static DioExceptionReadableStringBuilder readableStringBuilder =
defaultDioExceptionReadableStringBuilder;

/// Each exception can be override with a customized builder or fallback to
/// the default [DioException.readableStringBuilder].
DioExceptionReadableStringBuilder? stringBuilder;

/// Generate a new [DioException] by combining given values and original values.
DioException copyWith({
RequestOptions? requestOptions,
Expand All @@ -226,11 +235,12 @@ class DioException implements Exception {

@override
String toString() {
String msg = 'DioException [${type.toPrettyDescription()}]: $message';
if (error != null) {
msg += '\nError: $error';
try {
return stringBuilder?.call(this) ?? readableStringBuilder(this);
} catch (e, s) {
warningLog(e, s);
return defaultDioExceptionReadableStringBuilder(this);
}
return msg;
}

/// Because of [ValidateStatus] we need to consider all status codes when
Expand Down Expand Up @@ -278,3 +288,20 @@ class DioException implements Exception {
return buffer.toString();
}
}

/// The readable string builder's signature of
/// [DioException.readableStringBuilder].
typedef DioExceptionReadableStringBuilder = String Function(DioException e);

/// The default implementation of building a readable string of [DioException].
String defaultDioExceptionReadableStringBuilder(DioException e) {
final buffer = StringBuffer(
'DioException [${e.type.toPrettyDescription()}]: '
'${e.message}',
);
if (e.error != null) {
buffer.writeln();
buffer.write('Error: ${e.error}');
}
return buffer.toString();
}
4 changes: 2 additions & 2 deletions dio/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ Map<String, V> caseInsensitiveKeyMap<V>([Map<String, V>? value]) {
}

// TODO(Alex): Provide a configurable property on the Dio class once https://github.com/cfug/dio/discussions/1982 has made some progress.
void warningLog(String message, StackTrace stackTrace) {
void warningLog(Object message, StackTrace stackTrace) {
if (!kReleaseMode) {
dev.log(
message,
message.toString(),
level: 900,
name: '🔔 Dio',
stackTrace: stackTrace,
Expand Down
30 changes: 30 additions & 0 deletions dio/test/exception_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,34 @@ void main() {
},
testOn: '!browser',
);

test('DioExceptionReadableStringBuilder', () {
final requestOptions = RequestOptions(path: 'just/a/test', method: 'POST');
final exception = DioException(
requestOptions: requestOptions,
response: Response(requestOptions: requestOptions),
error: 'test',
message: 'test message',
stackTrace: StackTrace.current,
);
DioException.readableStringBuilder = (e) => 'Hey, Dio throws an exception: '
'${e.requestOptions.path}, '
'${e.requestOptions.method}, '
'${e.type}, '
'${e.error}, '
'${e.stackTrace}, '
'${e.message}';
expect(
exception.toString(),
'Hey, Dio throws an exception: '
'just/a/test, '
'POST, '
'DioExceptionType.unknown, '
'test, '
'${exception.stackTrace}, '
'test message',
);
exception.stringBuilder = (e) => 'Locally override: ${e.message}';
expect(exception.toString(), 'Locally override: test message');
});
}
Loading