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

feat(dio): Allow ResponseDecoder and RequestEncoder to be async #2015

Merged
merged 10 commits into from
Nov 10, 2023
1 change: 1 addition & 0 deletions dio/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ See the [Migration Guide][] for the complete breaking changes list.**

- Raise warning for `Map`s other than `Map<String, dynamic>` when encoding request data.
- Improve exception messages
- Allow `ResponseDecoder` and `RequestEncoder` to be async

## 5.3.3

Expand Down
8 changes: 7 additions & 1 deletion dio/lib/src/dio_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,13 @@ abstract class DioMixin implements Dio {
// Call the request transformer.
final transformed = await transformer.transformRequest(options);
if (options.requestEncoder != null) {
bytes = options.requestEncoder!(transformed, options);
final encoded = options.requestEncoder!(transformed, options);

if (encoded is Future) {
bytes = await encoded;
} else {
bytes = encoded;
}
} else {
// Converts the data to UTF-8 by default.
bytes = utf8.encode(transformed);
Expand Down
6 changes: 4 additions & 2 deletions dio/lib/src/options.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:meta/meta.dart';

import 'adapter.dart';
Expand Down Expand Up @@ -76,14 +78,14 @@ enum ListFormat {
typedef ValidateStatus = bool Function(int? status);

/// The type of a response decoding callback.
typedef ResponseDecoder = String? Function(
typedef ResponseDecoder = FutureOr<String?> Function(
List<int> responseBytes,
RequestOptions options,
ResponseBody responseBody,
);

/// The type of a response encoding callback.
typedef RequestEncoder = List<int> Function(
typedef RequestEncoder = FutureOr<List<int>> Function(
String request,
RequestOptions options,
);
Expand Down
8 changes: 7 additions & 1 deletion dio/lib/src/transformers/sync_transformer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,17 @@ class SyncTransformer extends Transformer {
);
final String? response;
if (options.responseDecoder != null) {
response = options.responseDecoder!(
final decodeResponse = options.responseDecoder!(
responseBytes,
options,
responseBody..stream = Stream.empty(),
);

if (decodeResponse is Future) {
response = await decodeResponse;
} else {
response = decodeResponse;
}
} else if (!isJsonContent || responseBytes.isNotEmpty) {
response = utf8.decode(responseBytes, allowMalformed: true);
} else {
Expand Down
46 changes: 46 additions & 0 deletions dio/test/options_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
@TestOn('vm')
import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:test/test.dart';

Expand Down Expand Up @@ -399,6 +401,50 @@ void main() {
expect(response.data, null);
});

test('responseDecoder can return Future<String?>', () async {
final dio = Dio();
dio.options.responseDecoder = (_, __, ___) => Future.value('example');
dio.options.baseUrl = EchoAdapter.mockBase;
dio.httpClientAdapter = EchoAdapter();

final Response response = await dio.get('');

expect(response.data, 'example');
});

test('responseDecoder can return String?', () async {
final dio = Dio();
dio.options.responseDecoder = (_, __, ___) => 'example';
dio.options.baseUrl = EchoAdapter.mockBase;
dio.httpClientAdapter = EchoAdapter();

final Response response = await dio.get('');

expect(response.data, 'example');
});

test('requestEncoder can return Future<List<int>>', () async {
final dio = Dio();
dio.options.requestEncoder = (data, _) => Future.value(utf8.encode(data));
dio.options.baseUrl = EchoAdapter.mockBase;
dio.httpClientAdapter = EchoAdapter();

final Response response = await dio.get('');

expect(response.statusCode, 200);
});

test('requestEncoder can return List<int>', () async {
final dio = Dio();
dio.options.requestEncoder = (data, _) => utf8.encode(data);
dio.options.baseUrl = EchoAdapter.mockBase;
dio.httpClientAdapter = EchoAdapter();

final Response response = await dio.get('');

expect(response.statusCode, 200);
});

test('invalid response type throws exceptions', () async {
final dio = Dio(
BaseOptions(
Expand Down