Index: sdk/lib/core/uri.dart |
diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart |
index e4a31aa575fd0f40d23012d7b8f1382cf17d279c..660c8c87a592a248494cfa93cde8c3244b6b5146 100644 |
--- a/sdk/lib/core/uri.dart |
+++ b/sdk/lib/core/uri.dart |
@@ -136,14 +136,16 @@ class Uri { |
* will be escaped if necessary. |
* When [queryParameters] is used the query is built from the |
* provided map. Each key and value in the map is percent-encoded |
- * and joined using equal and ampersand characters. The |
- * percent-encoding of the keys and values encodes all characters |
- * except for the unreserved characters. |
+ * and joined using equal and ampersand characters. |
+ * A value in the map must be either a string, or a an [Iterable] of strings, |
+ * where the latter corresponds to multiple values for the same key. |
+ * The percent-encoding of the keys and values encodes all characters |
+ * except for the unreserved characters, and replaces spaces with `+`. |
* If `query` is the empty string, it is equivalent to omitting it. |
* To have an actual empty query part, |
* use an empty list for `queryParameters`. |
- * If both `query` and `queryParameters` are omitted or `null`, the |
- * URI will have no query part. |
+ * If both `query` and `queryParameters` are omitted or `null`, |
+ * the URI will have no query part. |
* |
* The fragment component is set through [fragment]. |
* It should be a valid URI fragment, but invalid characters other than |
@@ -157,7 +159,7 @@ class Uri { |
String path, |
Iterable<String> pathSegments, |
String query, |
- Map<String, String> queryParameters, |
+ Map<String, dynamic> queryParameters, |
String fragment}) { |
scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); |
userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); |
@@ -1104,23 +1106,54 @@ class Uri { |
* Returns the URI query split into a map according to the rules |
* specified for FORM post in the [HTML 4.01 specification section |
* 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4"). |
- * Each key and value in the returned map has been decoded. If there is no |
- * query the empty map is returned. |
+ * Each key and value in the returned map has been decoded. |
+ * If there is no query the empty map is returned. |
* |
* Keys in the query string that have no value are mapped to the |
* empty string. |
+ * If a key occurs more than once in the query string, it is mapped to |
+ * one of the values. The [queryParameterValues] getter can provide a |
floitsch
2015/12/11 19:00:58
which one?
Lasse Reichstein Nielsen
2016/01/12 13:34:11
I really don't want to promise that.
It's likely g
|
+ * map that maps to all the values. |
* |
* The returned map is unmodifiable and will throw [UnsupportedError] on any |
* calls that would mutate it. |
*/ |
Map<String, String> get queryParameters { |
if (_queryParameters == null) { |
- _queryParameters = new UnmodifiableMapView(splitQueryString(query)); |
+ _queryParameters = |
+ new UnmodifiableMapView<String, String>(splitQueryString(query)); |
} |
return _queryParameters; |
} |
/** |
+ * Returns the URI query split into a map according to the rules |
+ * specified for FORM post in the [HTML 4.01 specification section |
+ * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4"). |
+ * Each key and value in the returned map has been decoded. If there is no |
+ * query the empty map is returned. |
+ * |
+ * Keys are mapped to lists of their values. If a key occurs only once, |
+ * its value is a singleton list. If a key occurs with no value, the |
+ * empty string is used as the value for that occurrence. |
+ * |
+ * The returned map and the lists it contains are unmodifiable |
+ * and will throw [UnsupportedError] on any calls that would mutate them. |
+ */ |
+ Map<String, List<String>> get queryParameterLists { |
+ if (_queryParameterLists == null) { |
+ Map queryParameterLists = _splitQueryStringAll(query); |
+ for (var key in queryParameterLists.keys) { |
+ queryParameterLists[key] = |
+ new List<String>.unmodifiable(queryParameterLists[key]); |
+ } |
+ _queryParameterLists = new Map<String, List<String>>.unmodifiable( |
+ queryParameterLists); |
+ } |
+ return _queryParameterLists; |
+ } |
+ |
+ /** |
* Returns a URI where the path has been normalized. |
* |
* A normalized path does not contain `.` segments or non-leading `..` |
@@ -1350,10 +1383,23 @@ class Uri { |
result.write("&"); |
} |
first = false; |
- result.write(Uri.encodeQueryComponent(key)); |
- if (value != null && !value.isEmpty) { |
- result.write("="); |
- result.write(Uri.encodeQueryComponent(value)); |
+ if (value == null) { |
Søren Gjesse
2015/12/14 07:41:00
Maybe this is OK, but if value is null or the empt
Lasse Reichstein Nielsen
2015/12/14 13:13:12
I think a "foo":[] entry should not introduce a "f
|
+ result.write(Uri.encodeQueryComponent(key)); |
+ } else if (value is String) { |
+ result.write(Uri.encodeQueryComponent(key)); |
+ if (value.isNotEmpty) { |
+ result.write("="); |
+ result.write(Uri.encodeQueryComponent(value)); |
+ } |
+ } else { |
+ Iterable values = value; |
+ for (String value in values) { |
+ result.write(Uri.encodeQueryComponent(key)); |
+ if (value != null && value.isNotEmpty) { |
+ result.write("="); |
+ result.write(Uri.encodeQueryComponent(value)); |
+ } |
+ } |
} |
}); |
return result.toString(); |
@@ -2156,6 +2202,43 @@ class Uri { |
}); |
} |
+ static List _createList() => []; |
+ |
+ static Map _splitQueryStringAll( |
+ String query, {Encoding encoding: UTF8}) { |
+ Map result = {}; |
+ int i = 0; |
+ int start = 0; |
+ int equalsIndex = -1; |
+ void parsePair(int start, int equalsIndex, int end) { |
floitsch
2015/12/11 19:00:58
new line before and after nested functions.
Lasse Reichstein Nielsen
2016/01/12 13:34:11
Done.
|
+ String key; |
+ String value; |
+ if (equalsIndex < 0) { |
+ key = _uriDecode(encodedComponent, start, end, encoding, true); |
+ value = ""; |
+ } else { |
+ key = |
+ _uriDecode(encodedComponent, start, equalsIndex, encoding, true); |
floitsch
2015/12/11 19:00:58
nit: it looks like this would fit on the previous
Lasse Reichstein Nielsen
2016/01/12 13:34:11
It does. Incredible!
|
+ value = |
+ _uriDecode(encodedComponent, equalsIndex + 1, end, encoding, true); |
+ } |
+ result.putIfAbsent(key, _createList).add(value); |
+ } |
+ while (i < query.length) { |
+ int char = query.codeUnitAt(i); |
+ if (char == _equals && equalsIndex < 0) { |
+ equalsIndex = i; |
+ } else if (i == _ampersand) { |
+ parsePair(start, equalsIndex, i); |
+ start = i + 1; |
+ equalsIndex = -1; |
+ } |
+ i++; |
+ } |
+ parsePair(start, equalsIndex, i); |
+ return result; |
+ } |
+ |
/** |
* Parse the [host] as an IP version 4 (IPv4) address, returning the address |
* as a list of 4 bytes in network byte order (big endian). |