diff --git a/dio/CHANGELOG.md b/dio/CHANGELOG.md index 1835a70b6..12d291071 100644 --- a/dio/CHANGELOG.md +++ b/dio/CHANGELOG.md @@ -2,6 +2,7 @@ ## 5.0.0-dev.1 +- Allow `data` in all request methods. - A platform independent `HttpClientAdapter` can now be instantiated by doing `dio.httpClientAdapter = HttpClientAdapter();`. - Add `ValidateCertificate` to handle certificate pinning better. diff --git a/dio/lib/src/dio.dart b/dio/lib/src/dio.dart index 08a5885ca..1c44657a4 100644 --- a/dio/lib/src/dio.dart +++ b/dio/lib/src/dio.dart @@ -61,6 +61,7 @@ abstract class Dio { /// Handy method to make http GET request, which is a alias of [dio.fetch(RequestOptions)]. Future> get( String path, { + Object? data, Map? queryParameters, Options? options, CancelToken? cancelToken, @@ -70,6 +71,7 @@ abstract class Dio { /// Handy method to make http GET request, which is a alias of [dio.fetch(RequestOptions)]. Future> getUri( Uri uri, { + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onReceiveProgress, diff --git a/dio/lib/src/dio_mixin.dart b/dio/lib/src/dio_mixin.dart index 6a0b055c5..937a00d32 100644 --- a/dio/lib/src/dio_mixin.dart +++ b/dio/lib/src/dio_mixin.dart @@ -51,12 +51,14 @@ abstract class DioMixin implements Dio { Future> get( String path, { Map? queryParameters, + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onReceiveProgress, }) { return request( path, + data: data, queryParameters: queryParameters, options: checkOptions('GET', options), onReceiveProgress: onReceiveProgress, @@ -68,12 +70,14 @@ abstract class DioMixin implements Dio { @override Future> getUri( Uri uri, { + Object? data, Options? options, CancelToken? cancelToken, ProgressCallback? onReceiveProgress, }) { return requestUri( uri, + data: data, options: checkOptions('GET', options), onReceiveProgress: onReceiveProgress, cancelToken: cancelToken, @@ -683,16 +687,19 @@ abstract class DioMixin implements Dio { throw ArgumentError.value(options.method, "method"); } final data = options.data; - List bytes; - Stream> stream; - const allowPayloadMethods = ['POST', 'PUT', 'PATCH', 'DELETE']; - if (data != null && allowPayloadMethods.contains(options.method)) { + if (data != null) { + final Stream> stream; // Handle the FormData int? length; if (data is Stream) { - assert(data is Stream, - 'Stream type must be `Stream`, but ${data.runtimeType} is found.'); - stream = data as Stream>; + if (data is! Stream>) { + throw ArgumentError.value( + data.runtimeType, + 'data', + 'Stream type must be `Stream>`', + ); + } + stream = data; options.headers.keys.any((String key) { if (key.toLowerCase() == Headers.contentLengthHeader) { length = int.parse(options.headers[key].toString()); @@ -708,6 +715,7 @@ abstract class DioMixin implements Dio { length = data.length; options.headers[Headers.contentLengthHeader] = length.toString(); } else { + final List bytes; // Call request transformer. final data = await transformer.transformRequest(options); if (options.requestEncoder != null) { diff --git a/dio/lib/src/options.dart b/dio/lib/src/options.dart index 7819a5c06..e011a2c7a 100644 --- a/dio/lib/src/options.dart +++ b/dio/lib/src/options.dart @@ -99,7 +99,6 @@ class BaseOptions extends _RequestConfig with OptionsMixin { RequestEncoder? requestEncoder, ResponseDecoder? responseDecoder, ListFormat? listFormat, - this.setRequestContentTypeWhenNoPayload = false, }) : assert(connectTimeout == null || !connectTimeout.isNegative), assert(baseUrl.isEmpty || Uri.parse(baseUrl).host.isNotEmpty), super( @@ -145,7 +144,6 @@ class BaseOptions extends _RequestConfig with OptionsMixin { RequestEncoder? requestEncoder, ResponseDecoder? responseDecoder, ListFormat? listFormat, - bool? setRequestContentTypeWhenNoPayload, }) { return BaseOptions( method: method ?? this.method, @@ -167,23 +165,8 @@ class BaseOptions extends _RequestConfig with OptionsMixin { requestEncoder: requestEncoder ?? this.requestEncoder, responseDecoder: responseDecoder ?? this.responseDecoder, listFormat: listFormat ?? this.listFormat, - setRequestContentTypeWhenNoPayload: setRequestContentTypeWhenNoPayload ?? - this.setRequestContentTypeWhenNoPayload, ); } - - static const _allowPayloadMethods = ['POST', 'PUT', 'PATCH', 'DELETE']; - - /// if false, content-type in request header will be deleted when method is not on of `_allowPayloadMethods` - bool setRequestContentTypeWhenNoPayload; - - String? contentTypeWithRequestBody(String method) { - if (setRequestContentTypeWhenNoPayload) { - return contentType; - } else { - return _allowPayloadMethods.contains(method) ? contentType : null; - } - } } mixin OptionsMixin { @@ -312,7 +295,7 @@ class Options { if (this.headers != null) { headers.addAll(this.headers!); } - String? contentType = this.headers?[Headers.contentTypeHeader]; + final String? contentType = this.headers?[Headers.contentTypeHeader]; final extra = Map.from(baseOpt.extra); if (this.extra != null) { extra.addAll(this.extra!); @@ -341,14 +324,11 @@ class Options { responseDecoder: responseDecoder ?? baseOpt.responseDecoder, listFormat: listFormat ?? baseOpt.listFormat, ); - requestOptions.onReceiveProgress = onReceiveProgress; requestOptions.onSendProgress = onSendProgress; requestOptions.cancelToken = cancelToken; - - requestOptions.contentType = contentType ?? - this.contentType ?? - baseOpt.contentTypeWithRequestBody(method); + requestOptions.contentType = + contentType ?? this.contentType ?? baseOpt.contentType; return requestOptions; } @@ -661,7 +641,7 @@ class _RequestConfig { }; this.responseType = responseType ?? ResponseType.json; if (!contentTypeInHeader) { - this.contentType = contentType ?? Headers.jsonContentType; + this.contentType = contentType; } } diff --git a/dio/migration_guide.md b/dio/migration_guide.md index 99bd150d7..c0cfa5357 100644 --- a/dio/migration_guide.md +++ b/dio/migration_guide.md @@ -17,6 +17,8 @@ When new content need to be added to the migration guide, make sure they're foll ### Summary +- `BaseOptions.setRequestContentTypeWhenNoPayload` has been removed. +- `get` and `getUri` in `Dio` has different signature. - `DefaultHttpClientAdapter` is now named `IOHttpClientAdapter`, and the platform independent adapter can be initiated by `HttpClientAdapter()` which is a factory method. - Adapters that extends `HttpClientAdapter` must now `implements` instead of `extends`. @@ -29,6 +31,30 @@ When new content need to be added to the migration guide, make sure they're foll ### Details +#### `get` and `getUri` + +```diff + Future> get( + String path, { ++ Object? data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onReceiveProgress, + }); +``` + +```diff + Future> getUri( + Uri uri, { ++ Object? data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onReceiveProgress, + }); +``` + #### `HttpClientAdapter` Before: diff --git a/dio/test/options_test.dart b/dio/test/options_test.dart index 9028e16a9..310fae903 100644 --- a/dio/test/options_test.dart +++ b/dio/test/options_test.dart @@ -124,7 +124,7 @@ void main() { assert(bo1.headers['content-type'] == contentType); assert(bo2.headers['content-type'] == contentType); - assert(bo3.headers['content-type'] == Headers.jsonContentType); + assert(bo3.headers['content-type'] == null); try { bo1.copyWith(headers: headers); @@ -213,27 +213,19 @@ void main() { dio.options.baseUrl = EchoAdapter.mockBase; dio.httpClientAdapter = EchoAdapter(); - Response r1 = await dio.get(''); - assert(r1.requestOptions.headers[Headers.contentTypeHeader] == null); - - dio.options.setRequestContentTypeWhenNoPayload = true; - - r1 = await dio.get(''); - assert( - r1.requestOptions.headers[Headers.contentTypeHeader] == - Headers.jsonContentType, + final r1 = await dio.get(''); + expect( + r1.requestOptions.headers[Headers.contentTypeHeader], + null, ); - dio.options.setRequestContentTypeWhenNoPayload = false; - final r2 = await dio.get( '', options: Options(contentType: Headers.jsonContentType), ); - - assert( - r2.requestOptions.headers[Headers.contentTypeHeader] == - Headers.jsonContentType, + expect( + r2.requestOptions.headers[Headers.contentTypeHeader], + Headers.jsonContentType, ); final r3 = await dio.get( @@ -242,21 +234,20 @@ void main() { Headers.contentTypeHeader: Headers.jsonContentType, }), ); - assert( - r3.requestOptions.headers[Headers.contentTypeHeader] == - Headers.jsonContentType, + expect( + r3.requestOptions.headers[Headers.contentTypeHeader], + Headers.jsonContentType, ); final r4 = await dio.post('', data: ''); - assert( - r4.requestOptions.headers[Headers.contentTypeHeader] == - Headers.jsonContentType, + expect( + r4.requestOptions.headers[Headers.contentTypeHeader], + null, ); }); test('#test default content-type 2', () async { final dio = Dio(); - dio.options.setRequestContentTypeWhenNoPayload = true; dio.options.baseUrl = 'https://www.example.com'; final r1 = Options(method: 'GET').compose(dio.options, '/test').copyWith( @@ -280,7 +271,6 @@ void main() { ); assert(false); } catch (_) {} - dio.options.setRequestContentTypeWhenNoPayload = false; final r3 = Options(method: 'GET').compose(dio.options, '/test'); assert(r3.uri.toString() == 'https://www.example.com/test'); @@ -349,4 +339,29 @@ void main() { testInvalidArgumentException("CONNECT$separator"); } }); + + test('Transform data correctly with requests', () async { + final dio = Dio() + ..httpClientAdapter = EchoAdapter() + ..options.baseUrl = EchoAdapter.mockBase; + const methods = [ + 'CONNECT', + 'HEAD', + 'GET', + 'POST', + 'PUT', + 'PATCH', + 'DELETE', + 'OPTIONS', + 'TRACE', + ]; + for (final method in methods) { + final response = await dio.request( + '/test', + data: 'test', + options: Options(method: method), + ); + expect(response.data, 'test'); + } + }); } diff --git a/dio/test/request_test.dart b/dio/test/request_test.dart index 3d51c3233..e5c856578 100644 --- a/dio/test/request_test.dart +++ b/dio/test/request_test.dart @@ -19,6 +19,7 @@ void main() { dio = Dio(); dio.options ..baseUrl = serverUrl.toString() + ..contentType = Headers.jsonContentType ..connectTimeout = Duration(seconds: 1) ..receiveTimeout = Duration(seconds: 5) ..headers = {'User-Agent': 'dartisan'}; diff --git a/plugins/http2_adapter/lib/src/http2_adapter.dart b/plugins/http2_adapter/lib/src/http2_adapter.dart index a636e701b..ff3b56066 100644 --- a/plugins/http2_adapter/lib/src/http2_adapter.dart +++ b/plugins/http2_adapter/lib/src/http2_adapter.dart @@ -50,7 +50,7 @@ class Http2Adapter implements HttpClientAdapter { path = '/' + path; } if (uri.query.trim().isNotEmpty) { - path += ('?' + uri.query); + path += '?' + uri.query; } final headers = [ Header.ascii(':method', options.method),