-
Notifications
You must be signed in to change notification settings - Fork 160
Description
Problem
See background in #18 and #478.
URLSearchParams was designed, not to hold URL query data, but instead to hold application/x-www-form-urlencoded data, i.e. the data that is sent to a server when submitting a HTML <form>.
Unfortunately, it was misnamed URLSearchParams instead of ApplicationXWWWFormURLEncodedParams. And, even more unfortunately, a property named searchParams was added to the URL class, which is an instance of the URLSearchParams class. Any attempts to use the searchParams class will give misleading information about the URL. And any attempts to manipulate it will change the contents of your URL's query string in unintended ways, converting values from a query string serialization (of the type produced by the URL parser) into an application/x-www-form-urlencoded serialization.
Some examples of how url.searchParams does not allow faithful introspection into the URL record:
const urlA = new URL('http://localhost:9999/segment?foo=bar/baz? boo');
const urlB = new URL('http://localhost:9999/segment?foo=bar%2Fbaz%3F%20boo');
// Not equal:
console.log(urlA.href); // "http://localhost:9999/segment?foo=bar/baz?%20boo"
console.log(urlB.href); // "http://localhost:9999/segment?foo=bar%2Fbaz%3F%20boo"
console.log(urlA.search); // "?foo=bar/baz?%20boo"
console.log(urlB.search); // "?foo=bar%2Fbaz%3F%20boo"
// Equal:
console.log(urlA.searchParams.get("foo")); // "bar/baz? boo"
console.log(urlB.searchParams.get("foo")); // "bar/baz? boo"
// Equal, but both different from search:
console.log(urlA.searchParams.toString()); // "foo=bar%2Fbaz%3F+boo"
console.log(urlB.searchParams.toString()); // "foo=bar%2Fbaz%3F+boo"Some examples of how using url.searchParams for mutation will cause unintended changes to your URL record:
const url = new URL('http://httpbin.org/anything?a=~');
console.log(url.href); // "http://httpbin.org/anything?a=~"
console.log(url.search); // "?a=~"
// This should be a no-op, but it is not:
url.searchParams.set("a", url.searchParams.get("a"));
console.log(url.href); // "http://httpbin.org/anything?a=%7E"const url = new URL('http://httpbin.org/anything?a=~');
console.log(url.href); // "http://httpbin.org/anything?a=~"
console.log(url.search); // "?a=~"
// This should not change the value of a, but it does:
url.searchParams.set("b", "d");
console.log(url.href); // "http://httpbin.org/anything?a=%7E&b=d"const url = new URL('http://httpbin.org/anything?a=b c');
console.log(url.href); // "http://httpbin.org/anything?a=b%20c"
console.log(url.search); // "?a=b%20c"
// This should be a no-op (sorting a single-element set), but it is not:
url.searchParams.sort();
console.log(url.href); // "http://httpbin.org/anything?a=b+c"Solution
In #478 (comment) I proposed four solutions to this problem. In response, @ricea (Chromium) and @achristensen07 (WebKit) indicated they were "in favor of maintaining the status quo". I interpret this as meaning that any changes to either the URL query string parser/serializer, or the application/x-www-form-urlencoded parser/serializer, or the URLSearchParams class and url.searchParams member, are not on the table.
Given these constraints, it seems the only thing we could do is propose a new non-breaking addition to the API. As such, I propose a URLQueryParams class and a corresponding url.queryParams member, which are identical to URLSearchParams and url.searchParams, except that they use the URL parsing/serialization rules instead of the application/x-www-form-urlencoded rules. (Alternate names include url.realSearchParams or url.searchParams2.)
With that added, we could effectively deprecate url.searchParams (i.e., state loudly in the spec and MDN that using it will give unreliable results and mess up your URLs), and note that URLSearchParams is useful for representing <form> serialization, but not useful for manipulating URL search parameters.
(Optionally, we might want to define url.query / location.query / workerLocation.query as aliases for the corresponding .search properties, to fully align on the "query" naming and obsolete the "search" naming. But that's separable.)