diff --git a/dio/CHANGELOG.md b/dio/CHANGELOG.md index f245beca8..b1db236df 100644 --- a/dio/CHANGELOG.md +++ b/dio/CHANGELOG.md @@ -5,6 +5,7 @@ See the [Migration Guide][] for the complete breaking changes list.** ## Unreleased +- Fix url concatenation '/' handling - Remove `http` from `dev_dependencies`. ## 5.2.1+1 diff --git a/dio/lib/src/options.dart b/dio/lib/src/options.dart index e43849216..756a66f37 100644 --- a/dio/lib/src/options.dart +++ b/dio/lib/src/options.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:meta/meta.dart'; import 'adapter.dart'; @@ -585,20 +587,21 @@ class RequestOptions extends _RequestConfig with OptionsMixin { /// generate uri Uri get uri { - String url = path; - if (!url.startsWith(RegExp(r'https?:'))) { - url = baseUrl + url; - final s = url.split(':/'); - if (s.length == 2) { - url = '${s[0]}:/${s[1].replaceAll('//', '/')}'; - } + Uri uri = Uri.parse(path); + + if (!uri.isAbsolute) { + uri = Uri.parse(baseUrl).resolveUri(uri); } + final query = Transformer.urlEncodeQueryMap(queryParameters, listFormat); if (query.isNotEmpty) { - url += (url.contains('?') ? '&' : '?') + query; + final separator = uri.query.isNotEmpty ? '&' : ''; + final mergedQuery = uri.query + separator + query; + uri = uri.replace(query: mergedQuery); } + // Normalize the url. - return Uri.parse(url).normalizePath(); + return uri.normalizePath(); } /// Request data, can be any type. diff --git a/dio/test/options_test.dart b/dio/test/options_test.dart index 7f18566e6..5c85eadc5 100644 --- a/dio/test/options_test.dart +++ b/dio/test/options_test.dart @@ -469,4 +469,88 @@ void main() { expect(response.data, 'test'); } }); + + test('Ensure consistent slash handling', () { + final dio = Dio(); + final inputs = [ + ['https://www.example.com', 'path'], + ['https://www.example.com/', 'path'], + ['https://www.example.com', '/path'], + ['https://www.example.com/', '/path'], + ]; + + for (final input in inputs) { + final baseUrl = input[0]; + final path = input[1]; + dio.options.baseUrl = baseUrl; + final actual = Options().compose(dio.options, path).uri.toString(); + expect(actual, equals('https://www.example.com/path')); + } + }); + + test('Should return absolute URI when path is already absolute', () { + final baseUrl = 'https://www.example.com'; + final path = 'https://www.another-example.com/path/to/resource'; + final baseOptions = BaseOptions(baseUrl: baseUrl); + + final actual = Options().compose(baseOptions, path).uri; + + expect(actual.toString(), equals(path)); + }); + + test('Should resolve relative path with base URL', () { + final baseUrl = 'https://www.example.com'; + final path = '/path/to/resource'; + final baseOptions = BaseOptions(baseUrl: baseUrl); + + final actual = Options().compose(baseOptions, path).uri; + + expect( + actual.toString(), + equals('https://www.example.com/path/to/resource'), + ); + }); + + test('Should add query parameters to the URI', () { + final baseUrl = 'https://www.example.com'; + final path = '/path/to/resource'; + final baseOptions = BaseOptions( + baseUrl: baseUrl, + queryParameters: {'param1': 'value1'}, + ); + + final actual = Options().compose( + baseOptions, + path, + queryParameters: {'param2': 'value2'}, + ).uri; + + expect( + actual.queryParameters, + equals({ + 'param1': 'value1', + 'param2': 'value2', + }), + ); + }); + + test('Should preserve path query parameters to the URI', () { + final baseUrl = 'https://www.example.com'; + final path = '/path/to/resource?param1=value1'; + final baseOptions = BaseOptions(baseUrl: baseUrl); + + final actual = Options(listFormat: ListFormat.csv).compose( + baseOptions, + path, + queryParameters: {'param2': 'value2'}, + ).uri; + + expect( + actual.queryParameters, + equals({ + 'param1': 'value1', + 'param2': 'value2', + }), + ); + }); }