DartのUri.httpsのqueryParametersはMap<String, dynamic>?ではない
タイトルそのままです。
一部のAPIはコードをちょっと追っかけて、仕様をチェックしましょう。
Dartのhttpパッケージを使ってGETリクエストをする場合の話です。
GETの場合、クエリパラメーターを組み立てる必要が生じます。
大抵の場合、URLが決まっているので Uri.parse
と文字列リテラルの組み合わせで対応できます。次のようなイメージです。
final url = Uri.parse('https://example.com?a=$b`);
final response = await http.get(url);
さて、パラメーターがnon-nullであれば良いのですが、nullableな場合に「文字列にnullを含めたくない」というお気持ちが生まれることがあります。https://example.com?a=null
ではなくhttps://example.com
としたい、という気持ちです。
気持ちの赴くままAPIを確認していると、Uri.http
とUri.https
が目につきます。StackOverFlowなんかを見ると評判が悪そうですが、queryParameters
のMapを操作してあげればnull
がURLの中に入ることもなさそうです。
/// Creates a new `https` URI from authority, path and query.
///
/// This constructor is the same as [Uri.http] except for the scheme
/// which is set to `https`.
factory Uri.https(String authority, String unencodedPath,
[Map<String, dynamic>? queryParameters]) = _Uri.https;
ところが、下記のようなコードを書くと動きません。
final keyString = "key";
final keyInt = 1;
final uri = Uri.https(
'example.com',
'list',
{
'keyString': keyString,
'keyInt': keyInt,
},
);
final response = await http.get(url);
一方で、下記のようにすると動きます。
final keyString = 'key';
final keyInt = 1;
final uri = Uri.https(
'example.com',
'list',
{
'keyString': keyString,
'keyInt': '$keyInt', <- here
},
);
final response = await http.get(url);
この原因を探るために、メソッドを追っていくと原因がわかります。
/// Implementation of [Uri.https].
factory _Uri.https(String authority, String unencodedPath,
[Map<String, dynamic>? queryParameters]) {
return _makeHttpUri("https", authority, unencodedPath, queryParameters);
}
static _Uri _makeHttpUri(String scheme, String? authority,
String unencodedPath, Map<String, dynamic>? queryParameters) {
~~~
return _Uri(
scheme: scheme,
userInfo: userInfo,
host: host,
port: port,
pathSegments: unencodedPath.split("/"),
queryParameters: queryParameters);
}
/// Implementation of [Uri.Uri].
factory _Uri(
{String? scheme,
String? userInfo,
String? host,
int? port,
String? path,
Iterable<String>? pathSegments,
String? query,
Map<String, dynamic /*String|Iterable<String>*/ >? queryParameters, <- here
String? fragment}) {
~~~
query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters);
~~~
return _Uri._internal(scheme, userInfo, host, port, path, query, fragment);
}
static String? _makeQuery(String? query, int start, int end,
Map<String, dynamic /*String|Iterable<String>*/ >? queryParameters) {
~~~
if (queryParameters == null) return null;
~~~
queryParameters.forEach((key, value) {
if (value == null || value is String) { <- here
writeParameter(key, value);
} else {
Iterable values = value;
for (String value in values) {
writeParameter(key, value);
}
}
});
return result.toString();
}
ということで、queryParameters
はMap<String, dynamic>?
で定義されていますが、内部的にMap<String, dynamic /*String|Iterable<String>*/ >?
として処理されているようです。
気になったのでIssue立ててみました。
何らかの形で直るといいな。
※追記
Issueに返ってきたコメントによると、factory Uri
のdocumentに書いてあるからそれを参照して欲しい(意訳)とのことでした。個人的には「う〜ん」という感じなのですが、ドキュメント全体を確認してやっていきましょう!
Discussion