Skip to content

Commit d5c678c

Browse files
authored
Tighten argument types to Uri (#507)
Closes #375 Make all arguments that were `Object` in order to allow either `String` or `Uri` accept only `Uri`. This gives better static checking for calling code.
1 parent 3845753 commit d5c678c

File tree

6 files changed

+58
-67
lines changed

6 files changed

+58
-67
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ null safety allow list.
1111

1212
* Add `const` constructor to `ByteStream`.
1313
* Migrate `BrowserClient` from `blob` to `arraybuffer`.
14+
* **BREAKING** All APIs which previously allowed a `String` or `Uri` to be
15+
passed now require a `Uri`.
1416
* **Breaking** Added a `body` and `encoding` argument to `Client.delete`. This
1517
is only breaking for implementations which override that method.
1618

example/main.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import 'package:http/http.dart' as http;
44
void main(List<String> arguments) async {
55
// This example uses the Google Books API to search for books about http.
66
// https://developers.google.com/books/docs/overview
7-
var url = 'https://www.googleapis.com/books/v1/volumes?q={http}';
7+
var url =
8+
Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': '{http}'});
89

910
// Await the http get response, then decode the json-formatted response.
1011
var response = await http.get(url);

lib/http.dart

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,27 @@ export 'src/response.dart';
2525
export 'src/streamed_request.dart';
2626
export 'src/streamed_response.dart';
2727

28-
/// Sends an HTTP HEAD request with the given headers to the given URL, which
29-
/// can be a [Uri] or a [String].
28+
/// Sends an HTTP HEAD request with the given headers to the given URL.
3029
///
3130
/// This automatically initializes a new [Client] and closes that client once
3231
/// the request is complete. If you're planning on making multiple requests to
3332
/// the same server, you should use a single [Client] for all of those requests.
3433
///
3534
/// For more fine-grained control over the request, use [Request] instead.
36-
Future<Response> head(Object url, {Map<String, String>? headers}) =>
35+
Future<Response> head(Uri url, {Map<String, String>? headers}) =>
3736
_withClient((client) => client.head(url, headers: headers));
3837

39-
/// Sends an HTTP GET request with the given headers to the given URL, which can
40-
/// be a [Uri] or a [String].
38+
/// Sends an HTTP GET request with the given headers to the given URL.
4139
///
4240
/// This automatically initializes a new [Client] and closes that client once
4341
/// the request is complete. If you're planning on making multiple requests to
4442
/// the same server, you should use a single [Client] for all of those requests.
4543
///
4644
/// For more fine-grained control over the request, use [Request] instead.
47-
Future<Response> get(Object url, {Map<String, String>? headers}) =>
45+
Future<Response> get(Uri url, {Map<String, String>? headers}) =>
4846
_withClient((client) => client.get(url, headers: headers));
4947

50-
/// Sends an HTTP POST request with the given headers and body to the given URL,
51-
/// which can be a [Uri] or a [String].
48+
/// Sends an HTTP POST request with the given headers and body to the given URL.
5249
///
5350
/// [body] sets the body of the request. It can be a [String], a [List<int>] or
5451
/// a [Map<String, String>]. If it's a String, it's encoded using [encoding] and
@@ -66,13 +63,12 @@ Future<Response> get(Object url, {Map<String, String>? headers}) =>
6663
///
6764
/// For more fine-grained control over the request, use [Request] or
6865
/// [StreamedRequest] instead.
69-
Future<Response> post(Object url,
66+
Future<Response> post(Uri url,
7067
{Map<String, String>? headers, Object? body, Encoding? encoding}) =>
7168
_withClient((client) =>
7269
client.post(url, headers: headers, body: body, encoding: encoding));
7370

74-
/// Sends an HTTP PUT request with the given headers and body to the given URL,
75-
/// which can be a [Uri] or a [String].
71+
/// Sends an HTTP PUT request with the given headers and body to the given URL.
7672
///
7773
/// [body] sets the body of the request. It can be a [String], a [List<int>] or
7874
/// a [Map<String, String>]. If it's a String, it's encoded using [encoding] and
@@ -90,13 +86,13 @@ Future<Response> post(Object url,
9086
///
9187
/// For more fine-grained control over the request, use [Request] or
9288
/// [StreamedRequest] instead.
93-
Future<Response> put(Object url,
89+
Future<Response> put(Uri url,
9490
{Map<String, String>? headers, Object? body, Encoding? encoding}) =>
9591
_withClient((client) =>
9692
client.put(url, headers: headers, body: body, encoding: encoding));
9793

9894
/// Sends an HTTP PATCH request with the given headers and body to the given
99-
/// URL, which can be a [Uri] or a [String].
95+
/// URL.
10096
///
10197
/// [body] sets the body of the request. It can be a [String], a [List<int>] or
10298
/// a [Map<String, String>]. If it's a String, it's encoded using [encoding] and
@@ -114,27 +110,25 @@ Future<Response> put(Object url,
114110
///
115111
/// For more fine-grained control over the request, use [Request] or
116112
/// [StreamedRequest] instead.
117-
Future<Response> patch(Object url,
113+
Future<Response> patch(Uri url,
118114
{Map<String, String>? headers, Object? body, Encoding? encoding}) =>
119115
_withClient((client) =>
120116
client.patch(url, headers: headers, body: body, encoding: encoding));
121117

122-
/// Sends an HTTP DELETE request with the given headers to the given URL, which
123-
/// can be a [Uri] or a [String].
118+
/// Sends an HTTP DELETE request with the given headers to the given URL.
124119
///
125120
/// This automatically initializes a new [Client] and closes that client once
126121
/// the request is complete. If you're planning on making multiple requests to
127122
/// the same server, you should use a single [Client] for all of those requests.
128123
///
129124
/// For more fine-grained control over the request, use [Request] instead.
130-
Future<Response> delete(Object url,
125+
Future<Response> delete(Uri url,
131126
{Map<String, String>? headers, Object? body, Encoding? encoding}) =>
132127
_withClient((client) =>
133128
client.delete(url, headers: headers, body: body, encoding: encoding));
134129

135-
/// Sends an HTTP GET request with the given headers to the given URL, which can
136-
/// be a [Uri] or a [String], and returns a Future that completes to the body of
137-
/// the response as a [String].
130+
/// Sends an HTTP GET request with the given headers to the given URL and
131+
/// returns a Future that completes to the body of the response as a [String].
138132
///
139133
/// The Future will emit a [ClientException] if the response doesn't have a
140134
/// success status code.
@@ -145,12 +139,12 @@ Future<Response> delete(Object url,
145139
///
146140
/// For more fine-grained control over the request and response, use [Request]
147141
/// instead.
148-
Future<String> read(Object url, {Map<String, String>? headers}) =>
142+
Future<String> read(Uri url, {Map<String, String>? headers}) =>
149143
_withClient((client) => client.read(url, headers: headers));
150144

151-
/// Sends an HTTP GET request with the given headers to the given URL, which can
152-
/// be a [Uri] or a [String], and returns a Future that completes to the body of
153-
/// the response as a list of bytes.
145+
/// Sends an HTTP GET request with the given headers to the given URL and
146+
/// returns a Future that completes to the body of the response as a list of
147+
/// bytes.
154148
///
155149
/// The Future will emit a [ClientException] if the response doesn't have a
156150
/// success status code.
@@ -161,7 +155,7 @@ Future<String> read(Object url, {Map<String, String>? headers}) =>
161155
///
162156
/// For more fine-grained control over the request and response, use [Request]
163157
/// instead.
164-
Future<Uint8List> readBytes(Object url, {Map<String, String>? headers}) =>
158+
Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) =>
165159
_withClient((client) => client.readBytes(url, headers: headers));
166160

167161
Future<T> _withClient<T>(Future<T> Function(Client) fn) async {

lib/src/base_client.dart

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,43 +19,42 @@ import 'streamed_response.dart';
1919
/// maybe [close], and then they get various convenience methods for free.
2020
abstract class BaseClient implements Client {
2121
@override
22-
Future<Response> head(Object url, {Map<String, String>? headers}) =>
22+
Future<Response> head(Uri url, {Map<String, String>? headers}) =>
2323
_sendUnstreamed('HEAD', url, headers);
2424

2525
@override
26-
Future<Response> get(Object url, {Map<String, String>? headers}) =>
26+
Future<Response> get(Uri url, {Map<String, String>? headers}) =>
2727
_sendUnstreamed('GET', url, headers);
2828

2929
@override
30-
Future<Response> post(Object url,
30+
Future<Response> post(Uri url,
3131
{Map<String, String>? headers, Object? body, Encoding? encoding}) =>
3232
_sendUnstreamed('POST', url, headers, body, encoding);
3333

3434
@override
35-
Future<Response> put(Object url,
35+
Future<Response> put(Uri url,
3636
{Map<String, String>? headers, Object? body, Encoding? encoding}) =>
3737
_sendUnstreamed('PUT', url, headers, body, encoding);
3838

3939
@override
40-
Future<Response> patch(Object url,
40+
Future<Response> patch(Uri url,
4141
{Map<String, String>? headers, Object? body, Encoding? encoding}) =>
4242
_sendUnstreamed('PATCH', url, headers, body, encoding);
4343

4444
@override
45-
Future<Response> delete(Object url,
45+
Future<Response> delete(Uri url,
4646
{Map<String, String>? headers, Object? body, Encoding? encoding}) =>
4747
_sendUnstreamed('DELETE', url, headers, body, encoding);
4848

4949
@override
50-
Future<String> read(Object url, {Map<String, String>? headers}) async {
50+
Future<String> read(Uri url, {Map<String, String>? headers}) async {
5151
final response = await get(url, headers: headers);
5252
_checkResponseSuccess(url, response);
5353
return response.body;
5454
}
5555

5656
@override
57-
Future<Uint8List> readBytes(Object url,
58-
{Map<String, String>? headers}) async {
57+
Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) async {
5958
final response = await get(url, headers: headers);
6059
_checkResponseSuccess(url, response);
6160
return response.bodyBytes;
@@ -73,9 +72,9 @@ abstract class BaseClient implements Client {
7372

7473
/// Sends a non-streaming [Request] and returns a non-streaming [Response].
7574
Future<Response> _sendUnstreamed(
76-
String method, url, Map<String, String>? headers,
75+
String method, Uri url, Map<String, String>? headers,
7776
[body, Encoding? encoding]) async {
78-
var request = Request(method, _fromUriOrString(url));
77+
var request = Request(method, url);
7978

8079
if (headers != null) request.headers.addAll(headers);
8180
if (encoding != null) request.encoding = encoding;
@@ -95,17 +94,15 @@ abstract class BaseClient implements Client {
9594
}
9695

9796
/// Throws an error if [response] is not successful.
98-
void _checkResponseSuccess(url, Response response) {
97+
void _checkResponseSuccess(Uri url, Response response) {
9998
if (response.statusCode < 400) return;
10099
var message = 'Request to $url failed with status ${response.statusCode}';
101100
if (response.reasonPhrase != null) {
102101
message = '$message: ${response.reasonPhrase}';
103102
}
104-
throw ClientException('$message.', _fromUriOrString(url));
103+
throw ClientException('$message.', url);
105104
}
106105

107106
@override
108107
void close() {}
109108
}
110-
111-
Uri _fromUriOrString(uri) => uri is String ? Uri.parse(uri) : uri as Uri;

lib/src/client.dart

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,18 @@ abstract class Client {
3131
/// `dart:html` is available, otherwise it will throw an unsupported error.
3232
factory Client() => createClient();
3333

34-
/// Sends an HTTP HEAD request with the given headers to the given URL, which
35-
/// can be a [Uri] or a [String].
34+
/// Sends an HTTP HEAD request with the given headers to the given URL.
3635
///
3736
/// For more fine-grained control over the request, use [send] instead.
38-
Future<Response> head(Object url, {Map<String, String>? headers});
37+
Future<Response> head(Uri url, {Map<String, String>? headers});
3938

40-
/// Sends an HTTP GET request with the given headers to the given URL, which
41-
/// can be a [Uri] or a [String].
39+
/// Sends an HTTP GET request with the given headers to the given URL.
4240
///
4341
/// For more fine-grained control over the request, use [send] instead.
44-
Future<Response> get(Object url, {Map<String, String>? headers});
42+
Future<Response> get(Uri url, {Map<String, String>? headers});
4543

4644
/// Sends an HTTP POST request with the given headers and body to the given
47-
/// URL, which can be a [Uri] or a [String].
45+
/// URL.
4846
///
4947
/// [body] sets the body of the request. It can be a [String], a [List<int>]
5048
/// or a [Map<String, String>]. If it's a String, it's encoded using
@@ -61,11 +59,11 @@ abstract class Client {
6159
/// [encoding] defaults to [utf8].
6260
///
6361
/// For more fine-grained control over the request, use [send] instead.
64-
Future<Response> post(Object url,
62+
Future<Response> post(Uri url,
6563
{Map<String, String>? headers, Object? body, Encoding? encoding});
6664

6765
/// Sends an HTTP PUT request with the given headers and body to the given
68-
/// URL, which can be a [Uri] or a [String].
66+
/// URL.
6967
///
7068
/// [body] sets the body of the request. It can be a [String], a [List<int>]
7169
/// or a [Map<String, String>]. If it's a String, it's encoded using
@@ -82,11 +80,11 @@ abstract class Client {
8280
/// [encoding] defaults to [utf8].
8381
///
8482
/// For more fine-grained control over the request, use [send] instead.
85-
Future<Response> put(Object url,
83+
Future<Response> put(Uri url,
8684
{Map<String, String>? headers, Object? body, Encoding? encoding});
8785

8886
/// Sends an HTTP PATCH request with the given headers and body to the given
89-
/// URL, which can be a [Uri] or a [String].
87+
/// URL.
9088
///
9189
/// [body] sets the body of the request. It can be a [String], a [List<int>]
9290
/// or a [Map<String, String>]. If it's a String, it's encoded using
@@ -103,37 +101,35 @@ abstract class Client {
103101
/// [encoding] defaults to [utf8].
104102
///
105103
/// For more fine-grained control over the request, use [send] instead.
106-
Future<Response> patch(Object url,
104+
Future<Response> patch(Uri url,
107105
{Map<String, String>? headers, Object? body, Encoding? encoding});
108106

109-
/// Sends an HTTP DELETE request with the given headers to the given URL,
110-
/// which can be a [Uri] or a [String].
107+
/// Sends an HTTP DELETE request with the given headers to the given URL.
111108
///
112109
/// For more fine-grained control over the request, use [send] instead.
113-
Future<Response> delete(Object url,
110+
Future<Response> delete(Uri url,
114111
{Map<String, String>? headers, Object? body, Encoding? encoding});
115112

116-
/// Sends an HTTP GET request with the given headers to the given URL, which
117-
/// can be a [Uri] or a [String], and returns a Future that completes to the
118-
/// body of the response as a String.
113+
/// Sends an HTTP GET request with the given headers to the given URL and
114+
/// returns a Future that completes to the body of the response as a String.
119115
///
120116
/// The Future will emit a [ClientException] if the response doesn't have a
121117
/// success status code.
122118
///
123119
/// For more fine-grained control over the request and response, use [send] or
124120
/// [get] instead.
125-
Future<String> read(Object url, {Map<String, String>? headers});
121+
Future<String> read(Uri url, {Map<String, String>? headers});
126122

127-
/// Sends an HTTP GET request with the given headers to the given URL, which
128-
/// can be a [Uri] or a [String], and returns a Future that completes to the
129-
/// body of the response as a list of bytes.
123+
/// Sends an HTTP GET request with the given headers to the given URL and
124+
/// returns a Future that completes to the body of the response as a list of
125+
/// bytes.
130126
///
131127
/// The Future will emit a [ClientException] if the response doesn't have a
132128
/// success status code.
133129
///
134130
/// For more fine-grained control over the request and response, use [send] or
135131
/// [get] instead.
136-
Future<Uint8List> readBytes(Object url, {Map<String, String>? headers});
132+
Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers});
137133

138134
/// Sends an HTTP request and asynchronously returns the response.
139135
Future<StreamedResponse> send(BaseRequest request);

test/mock_client_test.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ void main() {
1616
json.encode(request.bodyFields), 200,
1717
request: request, headers: {'content-type': 'application/json'}));
1818

19-
var response = await client.post('http://example.com/foo',
19+
var response = await client.post(Uri.http('example.com', '/foo'),
2020
body: {'field1': 'value1', 'field2': 'value2'});
2121
expect(
2222
response.body, parse(equals({'field1': 'value1', 'field2': 'value2'})));
@@ -30,7 +30,7 @@ void main() {
3030
return http.StreamedResponse(stream, 200);
3131
});
3232

33-
var uri = Uri.parse('http://example.com/foo');
33+
var uri = Uri.http('example.com', '/foo');
3434
var request = http.Request('POST', uri)..body = 'hello, world';
3535
var streamedResponse = await client.send(request);
3636
var response = await http.Response.fromStream(streamedResponse);
@@ -40,6 +40,7 @@ void main() {
4040
test('handles a request with no body', () async {
4141
var client = MockClient((_) async => http.Response('you did it', 200));
4242

43-
expect(await client.read('http://example.com/foo'), equals('you did it'));
43+
expect(await client.read(Uri.http('example.com', '/foo')),
44+
equals('you did it'));
4445
});
4546
}

0 commit comments

Comments
 (0)