mirror of
https://github.com/nodejs/node.git
synced 2025-05-07 13:47:16 +00:00
Get rid of PHP/Rails style parameter munging.
- Handle only the most basic of query string parsing and construction. Leave the rest (e.g. Rails/PHP behaviors) to modules higher up the stack, like Express.
This commit is contained in:
parent
debf389490
commit
422d3c93bc
@ -2988,9 +2988,10 @@ Take a base URL, and a href URL, and resolve them as a browser would for an anch
|
|||||||
|
|
||||||
This module provides utilities for dealing with query strings. It provides the following methods:
|
This module provides utilities for dealing with query strings. It provides the following methods:
|
||||||
|
|
||||||
### querystring.stringify(obj, sep='&', eq='=', munge=true)
|
### querystring.stringify(obj, sep='&', eq='=')
|
||||||
|
|
||||||
Serialize an object to a query string. Optionally override the default separator and assignment characters.
|
Serialize an object to a query string. Optionally override the default separator and assignment characters.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
querystring.stringify({foo: 'bar'})
|
querystring.stringify({foo: 'bar'})
|
||||||
@ -3001,40 +3002,18 @@ Example:
|
|||||||
// returns
|
// returns
|
||||||
'foo:bar;baz:bob'
|
'foo:bar;baz:bob'
|
||||||
|
|
||||||
By default, this function will perform PHP/Rails-style parameter munging for arrays and objects used as
|
|
||||||
values within `obj`.
|
|
||||||
Example:
|
|
||||||
|
|
||||||
querystring.stringify({foo: ['bar', 'baz', 'boz']})
|
|
||||||
// returns
|
|
||||||
'foo%5B%5D=bar&foo%5B%5D=baz&foo%5B%5D=boz'
|
|
||||||
|
|
||||||
querystring.stringify({foo: {bar: 'baz'}})
|
|
||||||
// returns
|
|
||||||
'foo%5Bbar%5D=baz'
|
|
||||||
|
|
||||||
If you wish to disable the array munging (e.g. when generating parameters for a Java servlet), you
|
|
||||||
can set the `munge` argument to `false`.
|
|
||||||
Example:
|
|
||||||
|
|
||||||
querystring.stringify({foo: ['bar', 'baz', 'boz']}, '&', '=', false)
|
|
||||||
// returns
|
|
||||||
'foo=bar&foo=baz&foo=boz'
|
|
||||||
|
|
||||||
Note that when `munge` is `false`, parameter names with object values will still be munged.
|
|
||||||
|
|
||||||
### querystring.parse(str, sep='&', eq='=')
|
### querystring.parse(str, sep='&', eq='=')
|
||||||
|
|
||||||
Deserialize a query string to an object. Optionally override the default separator and assignment characters.
|
Deserialize a query string to an object. Optionally override the default separator and assignment characters.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
querystring.parse('a=b&b=c')
|
querystring.parse('a=b&b=c')
|
||||||
// returns
|
// returns
|
||||||
{ 'a': 'b'
|
{ 'a': 'b'
|
||||||
, 'b': 'c'
|
, 'b': 'c'
|
||||||
}
|
}
|
||||||
|
|
||||||
This function can parse both munged and unmunged query strings (see `stringify` for details).
|
|
||||||
|
|
||||||
### querystring.escape
|
### querystring.escape
|
||||||
|
|
||||||
The escape function used by `querystring.stringify`, provided so that it could be overridden if necessary.
|
The escape function used by `querystring.stringify`, provided so that it could be overridden if necessary.
|
||||||
|
@ -10,8 +10,22 @@ QueryString.escape = function (str) {
|
|||||||
return encodeURIComponent(str);
|
return encodeURIComponent(str);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var stringifyPrimitive = function(v) {
|
||||||
|
switch (typeof v) {
|
||||||
|
case "string":
|
||||||
|
return v;
|
||||||
|
|
||||||
|
case "boolean":
|
||||||
|
return v ? "true" : "false";
|
||||||
|
|
||||||
|
case "number":
|
||||||
|
return isFinite(v) ? v : "";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var stack = [];
|
|
||||||
/**
|
/**
|
||||||
* <p>Converts an arbitrary value to a Query String representation.</p>
|
* <p>Converts an arbitrary value to a Query String representation.</p>
|
||||||
*
|
*
|
||||||
@ -21,92 +35,61 @@ var stack = [];
|
|||||||
* @param obj {Variant} any arbitrary value to convert to query string
|
* @param obj {Variant} any arbitrary value to convert to query string
|
||||||
* @param sep {String} (optional) Character that should join param k=v pairs together. Default: "&"
|
* @param sep {String} (optional) Character that should join param k=v pairs together. Default: "&"
|
||||||
* @param eq {String} (optional) Character that should join keys to their values. Default: "="
|
* @param eq {String} (optional) Character that should join keys to their values. Default: "="
|
||||||
* @param munge {Boolean} (optional) Indicate whether array/object params should be munged, PHP/Rails-style. Default: true
|
|
||||||
* @param name {String} (optional) Name of the current key, for handling children recursively.
|
* @param name {String} (optional) Name of the current key, for handling children recursively.
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
QueryString.stringify = QueryString.encode = function (obj, sep, eq, munge, name) {
|
QueryString.stringify = QueryString.encode = function (obj, sep, eq, name) {
|
||||||
munge = typeof munge == "undefined" || munge;
|
|
||||||
sep = sep || "&";
|
sep = sep || "&";
|
||||||
eq = eq || "=";
|
eq = eq || "=";
|
||||||
var type = Object.prototype.toString.call(obj);
|
obj = (obj === null) ? undefined : obj;
|
||||||
if (obj == null || type == "[object Function]" || type == "[object Number]" && !isFinite(obj)) {
|
|
||||||
return name ? QueryString.escape(name) + eq : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
switch (typeof obj) {
|
||||||
case '[object Boolean]':
|
case "object":
|
||||||
obj = +obj; // fall through
|
return Object.keys(obj).map(function(k) {
|
||||||
case '[object Number]':
|
if (Array.isArray(obj[k])) {
|
||||||
case '[object String]':
|
return obj[k].map(function(v) {
|
||||||
return QueryString.escape(name) + eq + QueryString.escape(obj);
|
return QueryString.escape(stringifyPrimitive(k)) +
|
||||||
case '[object Array]':
|
eq +
|
||||||
name = name + (munge ? "[]" : "");
|
QueryString.escape(stringifyPrimitive(v));
|
||||||
return obj.map(function (item) {
|
}).join(sep);
|
||||||
return QueryString.stringify(item, sep, eq, munge, name);
|
} else {
|
||||||
}).join(sep);
|
return QueryString.escape(stringifyPrimitive(k)) +
|
||||||
}
|
eq +
|
||||||
// now we know it's an object.
|
QueryString.escape(stringifyPrimitive(obj[k]));
|
||||||
|
}
|
||||||
// Check for cyclical references in nested objects
|
|
||||||
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] === obj) {
|
|
||||||
throw new Error("querystring.stringify. Cyclical reference");
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.push(obj);
|
|
||||||
|
|
||||||
var begin = name ? name + "[" : "",
|
|
||||||
end = name ? "]" : "",
|
|
||||||
keys = Object.keys(obj),
|
|
||||||
n,
|
|
||||||
s = Object.keys(obj).map(function (key) {
|
|
||||||
n = begin + key + end;
|
|
||||||
return QueryString.stringify(obj[key], sep, eq, munge, n);
|
|
||||||
}).join(sep);
|
}).join(sep);
|
||||||
|
|
||||||
stack.pop();
|
default:
|
||||||
|
return (name) ?
|
||||||
if (!s && name) {
|
QueryString.escape(stringifyPrimitive(name)) + eq +
|
||||||
return name + "=";
|
QueryString.escape(stringifyPrimitive(obj)) :
|
||||||
|
"";
|
||||||
}
|
}
|
||||||
return s;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// matches .xxxxx or [xxxxx] or ['xxxxx'] or ["xxxxx"] with optional [] at the end
|
|
||||||
var chunks = /(?:(?:^|\.)([^\[\(\.]+)(?=\[|\.|$|\()|\[([^"'][^\]]*?)\]|\["([^\]"]*?)"\]|\['([^\]']*?)'\])(\[\])?/g;
|
|
||||||
// Parse a key=val string.
|
// Parse a key=val string.
|
||||||
QueryString.parse = QueryString.decode = function (qs, sep, eq) {
|
QueryString.parse = QueryString.decode = function (qs, sep, eq) {
|
||||||
|
sep = sep || "&";
|
||||||
|
eq = eq || "=";
|
||||||
var obj = {};
|
var obj = {};
|
||||||
if (qs === undefined) { return {} }
|
|
||||||
String(qs).split(sep || "&").map(function (keyValue) {
|
if (typeof qs !== 'string') {
|
||||||
var res = obj,
|
return obj;
|
||||||
next,
|
}
|
||||||
kv = keyValue.split(eq || "="),
|
|
||||||
key = QueryString.unescape(kv.shift(), true),
|
qs.split(sep).forEach(function(kvp) {
|
||||||
value = QueryString.unescape(kv.join(eq || "="), true);
|
var x = kvp.split(eq);
|
||||||
key.replace(chunks, function (all, name, nameInBrackets, nameIn2Quotes, nameIn1Quotes, isArray, offset) {
|
var k = QueryString.unescape(x[0], true);
|
||||||
var end = offset + all.length == key.length;
|
var v = QueryString.unescape(x.slice(1).join(eq), true);
|
||||||
name = name || nameInBrackets || nameIn2Quotes || nameIn1Quotes;
|
|
||||||
next = end ? value : {};
|
if (!(k in obj)) {
|
||||||
if (Array.isArray(res[name])) {
|
obj[k] = v;
|
||||||
res[name].push(next);
|
} else if (!Array.isArray(obj[k])) {
|
||||||
res = next;
|
obj[k] = [obj[k], v];
|
||||||
} else {
|
} else {
|
||||||
if (name in res) {
|
obj[k].push(v);
|
||||||
if (isArray || end) {
|
}
|
||||||
res = (res[name] = [res[name], next])[1];
|
|
||||||
} else {
|
|
||||||
res = res[name];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isArray) {
|
|
||||||
res = (res[name] = [next])[0];
|
|
||||||
} else {
|
|
||||||
res = res[name] = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
@ -10,35 +10,17 @@ var qs = require("querystring");
|
|||||||
var qsTestCases = [
|
var qsTestCases = [
|
||||||
["foo=918854443121279438895193", "foo=918854443121279438895193", {"foo": "918854443121279438895193"}],
|
["foo=918854443121279438895193", "foo=918854443121279438895193", {"foo": "918854443121279438895193"}],
|
||||||
["foo=bar", "foo=bar", {"foo" : "bar"}],
|
["foo=bar", "foo=bar", {"foo" : "bar"}],
|
||||||
["foo=bar&foo=quux", "foo%5B%5D=bar&foo%5B%5D=quux", {"foo" : ["bar", "quux"]}],
|
["foo=bar&foo=quux", "foo=bar&foo=quux", {"foo" : ["bar", "quux"]}],
|
||||||
["foo=1&bar=2", "foo=1&bar=2", {"foo" : "1", "bar" : "2"}],
|
["foo=1&bar=2", "foo=1&bar=2", {"foo" : "1", "bar" : "2"}],
|
||||||
["my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F", "my%20weird%20field=q1!2%22'w%245%267%2Fz8)%3F", {"my weird field" : "q1!2\"'w$5&7/z8)?" }],
|
["my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F", "my%20weird%20field=q1!2%22'w%245%267%2Fz8)%3F", {"my weird field" : "q1!2\"'w$5&7/z8)?" }],
|
||||||
["foo%3Dbaz=bar", "foo%3Dbaz=bar", {"foo=baz" : "bar"}],
|
["foo%3Dbaz=bar", "foo%3Dbaz=bar", {"foo=baz" : "bar"}],
|
||||||
["foo=baz=bar", "foo=baz%3Dbar", {"foo" : "baz=bar"}],
|
["foo=baz=bar", "foo=baz%3Dbar", {"foo" : "baz=bar"}],
|
||||||
[ "str=foo&arr[]=1&arr[]=2&arr[]=3&obj[a]=bar&obj[b][]=4&obj[b][]=5&obj[b][]=6&obj[b][]=&obj[c][]=4&obj[c][]=5&obj[c][][somestr]=baz&obj[objobj][objobjstr]=blerg&somenull=&undef=", "str=foo&arr%5B%5D=1&arr%5B%5D=2&arr%5B%5D=3&obj%5Ba%5D=bar&obj%5Bb%5D%5B%5D=4&obj%5Bb%5D%5B%5D=5&obj%5Bb%5D%5B%5D=6&obj%5Bb%5D%5B%5D=&obj%5Bc%5D%5B%5D=4&obj%5Bc%5D%5B%5D=5&obj%5Bc%5D%5B%5D%5Bsomestr%5D=baz&obj%5Bobjobj%5D%5Bobjobjstr%5D=blerg&somenull=&undef=", {
|
[ "str=foo&arr=1&arr=2&arr=3&somenull=&undef=", "str=foo&arr=1&arr=2&arr=3&somenull=&undef=", {
|
||||||
"str":"foo",
|
"str":"foo",
|
||||||
"arr":["1","2","3"],
|
"arr":["1","2","3"],
|
||||||
"obj":{
|
|
||||||
"a":"bar",
|
|
||||||
"b":["4","5","6",""],
|
|
||||||
"c":["4","5",{"somestr":"baz"}],
|
|
||||||
"objobj":{"objobjstr":"blerg"}
|
|
||||||
},
|
|
||||||
"somenull":"",
|
"somenull":"",
|
||||||
"undef":""
|
"undef":""
|
||||||
}],
|
}],
|
||||||
["foo[bar][bla]=baz&foo[bar][bla]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
|
|
||||||
["foo[bar][][bla]=baz&foo[bar][][bla]=blo", "foo%5Bbar%5D%5B%5D%5Bbla%5D=baz&foo%5Bbar%5D%5B%5D%5Bbla%5D=blo", {"foo":{"bar":[{"bla":"baz"},{"bla":"blo"}]}}],
|
|
||||||
["foo[bar][bla][]=baz&foo[bar][bla][]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
|
|
||||||
|
|
||||||
["foo.bar.bla=baz&foo.bar.bla=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
|
|
||||||
["foo.bar[].bla=baz&foo[bar][][bla]=blo", "foo%5Bbar%5D%5B%5D%5Bbla%5D=baz&foo%5Bbar%5D%5B%5D%5Bbla%5D=blo", {"foo":{"bar":[{"bla":"baz"},{"bla":"blo"}]}}],
|
|
||||||
["foo[bar].bla[]=baz&foo.bar[bla][]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
|
|
||||||
|
|
||||||
["foo['bar']['bla']=baz&foo[\"bar\"][\"bla\"]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
|
|
||||||
["foo['bar'][]['bla']=baz&foo['bar'][][\"bla\"]=blo", "foo%5Bbar%5D%5B%5D%5Bbla%5D=baz&foo%5Bbar%5D%5B%5D%5Bbla%5D=blo", {"foo":{"bar":[{"bla":"baz"},{"bla":"blo"}]}}],
|
|
||||||
["foo[bar][\"bla\"][]=baz&foo[\"bar\"][bla][]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
|
|
||||||
|
|
||||||
[" foo = bar ", "%20foo%20=%20bar%20", {" foo ":" bar "}],
|
[" foo = bar ", "%20foo%20=%20bar%20", {" foo ":" bar "}],
|
||||||
["foo=%zx", "foo=%25zx", {"foo":"%zx"}],
|
["foo=%zx", "foo=%25zx", {"foo":"%zx"}],
|
||||||
["foo=%EF%BF%BD", "foo=%EF%BF%BD", {"foo" : "\ufffd" }]
|
["foo=%EF%BF%BD", "foo=%EF%BF%BD", {"foo" : "\ufffd" }]
|
||||||
@ -47,7 +29,7 @@ var qsTestCases = [
|
|||||||
// [ wonkyQS, canonicalQS, obj ]
|
// [ wonkyQS, canonicalQS, obj ]
|
||||||
var qsColonTestCases = [
|
var qsColonTestCases = [
|
||||||
["foo:bar", "foo:bar", {"foo":"bar"}],
|
["foo:bar", "foo:bar", {"foo":"bar"}],
|
||||||
["foo:bar;foo:quux", "foo%5B%5D:bar;foo%5B%5D:quux", {"foo" : ["bar", "quux"]}],
|
["foo:bar;foo:quux", "foo:bar;foo:quux", {"foo" : ["bar", "quux"]}],
|
||||||
["foo:1&bar:2;baz:quux", "foo:1%26bar%3A2;baz:quux", {"foo":"1&bar:2", "baz":"quux"}],
|
["foo:1&bar:2;baz:quux", "foo:1%26bar%3A2;baz:quux", {"foo":"1&bar:2", "baz":"quux"}],
|
||||||
["foo%3Abaz:bar", "foo%3Abaz:bar", {"foo:baz":"bar"}],
|
["foo%3Abaz:bar", "foo%3Abaz:bar", {"foo:baz":"bar"}],
|
||||||
["foo:baz:bar", "foo:baz%3Abar", {"foo":"baz:bar"}]
|
["foo:baz:bar", "foo:baz%3Abar", {"foo":"baz:bar"}]
|
||||||
@ -65,8 +47,8 @@ var qsWeirdObjects = [
|
|||||||
[ {e:extendedFunction}, "e=", {"e":""} ],
|
[ {e:extendedFunction}, "e=", {"e":""} ],
|
||||||
[ {d:new Date()}, "d=", {"d":""} ],
|
[ {d:new Date()}, "d=", {"d":""} ],
|
||||||
[ {d:Date}, "d=", {"d":""} ],
|
[ {d:Date}, "d=", {"d":""} ],
|
||||||
[ {f:new Boolean(false), t:new Boolean(true)}, "f=0&t=1", {"f":"0", "t":"1"} ],
|
[ {f:new Boolean(false), t:new Boolean(true)}, "f=&t=", {"f":"", "t":""} ],
|
||||||
[ {f:false, t:true}, "f=0&t=1", {"f":"0", "t":"1"} ],
|
[ {f:false, t:true}, "f=false&t=true", {"f":"false", "t":"true"} ],
|
||||||
[ {n:null}, "n=", {"n":""} ],
|
[ {n:null}, "n=", {"n":""} ],
|
||||||
[ {nan:NaN}, "nan=", {"nan":""} ],
|
[ {nan:NaN}, "nan=", {"nan":""} ],
|
||||||
[ {inf:Infinity}, "inf=", {"inf":""} ]
|
[ {inf:Infinity}, "inf=", {"inf":""} ]
|
||||||
@ -84,7 +66,7 @@ var qsNoMungeTestCases = [
|
|||||||
["gragh=1&gragh=3&goo=2", {"gragh": ["1", "3"], "goo": "2"}],
|
["gragh=1&gragh=3&goo=2", {"gragh": ["1", "3"], "goo": "2"}],
|
||||||
["frappucino=muffin&goat%5B%5D=scone&pond=moose",
|
["frappucino=muffin&goat%5B%5D=scone&pond=moose",
|
||||||
{"frappucino": "muffin", "goat[]": "scone", "pond": "moose"}],
|
{"frappucino": "muffin", "goat[]": "scone", "pond": "moose"}],
|
||||||
["obj%5Btrololol%5D=yes&obj%5Blololo%5D=no", {"obj": {"trololol": "yes", "lololo": "no"}}]
|
["trololol=yes&lololo=no", {"trololol": "yes", "lololo": "no"}]
|
||||||
];
|
];
|
||||||
|
|
||||||
assert.strictEqual("918854443121279438895193", qs.parse("id=918854443121279438895193").id);
|
assert.strictEqual("918854443121279438895193", qs.parse("id=918854443121279438895193").id);
|
||||||
@ -123,11 +105,6 @@ qsNoMungeTestCases.forEach(function (testCase) {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
// now test stringifying
|
// now test stringifying
|
||||||
assert.throws(function () {
|
|
||||||
var f = {};
|
|
||||||
f.f = f;
|
|
||||||
qs.stringify(f);
|
|
||||||
});
|
|
||||||
|
|
||||||
// basic
|
// basic
|
||||||
qsTestCases.forEach(function (testCase) {
|
qsTestCases.forEach(function (testCase) {
|
||||||
|
Loading…
Reference in New Issue
Block a user