Skip to content

Commit 4729202

Browse files
fengmk2tjfontaine
authored andcommitted
querystring: custom encode and decode
Not all querystring are utf-8 encoding, make querystring can be used to encode / decode `non-utf8` encoding string if necessary. Signed-off-by: Timothy J Fontaine <tjfontaine@gmail.com>
1 parent f09b027 commit 4729202

3 files changed

Lines changed: 56 additions & 8 deletions

File tree

‎doc/api/querystring.markdown‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
This module provides utilities for dealing with query strings.
88
It provides the following methods:
99

10-
## querystring.stringify(obj, [sep], [eq])
10+
## querystring.stringify(obj, [sep], [eq], [options])
1111

1212
Serialize an object to a query string.
1313
Optionally override the default separator (`'&'`) and assignment (`'='`)
1414
characters.
1515

16+
Options object may contain `encodeURIComponent` property (`querystring.escape` by default),
17+
it can be used to encode string with `non-utf8` encoding if necessary.
18+
1619
Example:
1720

1821
querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' })
@@ -23,6 +26,13 @@ Example:
2326
// returns
2427
'foo:bar;baz:qux'
2528

29+
// Suppose gbkEncodeURIComponent function already exists,
30+
// it can encode string with `gbk` encoding
31+
querystring.stringify({ w: '中文', foo: 'bar' }, null, null,
32+
{ encodeURIComponent: gbkEncodeURIComponent })
33+
// returns
34+
'w=%D6%D0%CE%C4&foo=bar'
35+
2636
## querystring.parse(str, [sep], [eq], [options])
2737

2838
Deserialize a query string to an object.
@@ -32,12 +42,22 @@ characters.
3242
Options object may contain `maxKeys` property (equal to 1000 by default), it'll
3343
be used to limit processed keys. Set it to 0 to remove key count limitation.
3444

45+
Options object may contain `decodeURIComponent` property (`decodeURIComponent` by default),
46+
it can be used to decode `non-utf8` encoding string if necessary.
47+
3548
Example:
3649

3750
querystring.parse('foo=bar&baz=qux&baz=quux&corge')
3851
// returns
3952
{ foo: 'bar', baz: ['qux', 'quux'], corge: '' }
4053

54+
// Suppose gbkDecodeURIComponent function already exists,
55+
// it can decode `gbk` encoding string
56+
querystring.parse('w=%D6%D0%CE%C4&foo=bar', null, null,
57+
{ decodeURIComponent: gbkDecodeURIComponent })
58+
// returns
59+
{ w: '中文', foo: 'bar' }
60+
4161
## querystring.escape
4262

4363
The escape function used by `querystring.stringify`,

‎lib/querystring.js‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,25 +125,29 @@ var stringifyPrimitive = function(v) {
125125
};
126126

127127

128-
QueryString.stringify = QueryString.encode = function(obj, sep, eq) {
128+
QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) {
129129
sep = sep || '&';
130130
eq = eq || '=';
131131
if (util.isNull(obj)) {
132132
obj = undefined;
133133
}
134134

135+
var encode = QueryString.escape;
136+
if (options && typeof options.encodeURIComponent === 'function') {
137+
encode = options.encodeURIComponent;
138+
}
139+
135140
if (util.isObject(obj)) {
136141
return Object.keys(obj).map(function(k) {
137-
var ks = QueryString.escape(stringifyPrimitive(k)) + eq;
142+
var ks = encode(stringifyPrimitive(k)) + eq;
138143
if (util.isArray(obj[k])) {
139144
return obj[k].map(function(v) {
140-
return ks + QueryString.escape(stringifyPrimitive(v));
145+
return ks + encode(stringifyPrimitive(v));
141146
}).join(sep);
142147
} else {
143-
return ks + QueryString.escape(stringifyPrimitive(obj[k]));
148+
return ks + encode(stringifyPrimitive(obj[k]));
144149
}
145150
}).join(sep);
146-
147151
}
148152
return '';
149153
};
@@ -172,6 +176,11 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
172176
len = maxKeys;
173177
}
174178

179+
var decode = decodeURIComponent;
180+
if (options && typeof options.decodeURIComponent === 'function') {
181+
decode = options.decodeURIComponent;
182+
}
183+
175184
for (var i = 0; i < len; ++i) {
176185
var x = qs[i].replace(regexp, '%20'),
177186
idx = x.indexOf(eq),
@@ -186,8 +195,8 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
186195
}
187196

188197
try {
189-
k = decodeURIComponent(kstr);
190-
v = decodeURIComponent(vstr);
198+
k = decode(kstr);
199+
v = decode(vstr);
191200
} catch (e) {
192201
k = QueryString.unescape(kstr, true);
193202
v = QueryString.unescape(vstr, true);

‎test/simple/test-querystring.js‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,22 @@ assert.equal(0xeb, b[16]);
228228
assert.equal(0xd8, b[17]);
229229
assert.equal(0xa2, b[18]);
230230
assert.equal(0xe6, b[19]);
231+
232+
233+
// Test custom decode
234+
function demoDecode(str) {
235+
return str + str;
236+
}
237+
assert.deepEqual(
238+
qs.parse('a=a&b=b&c=c', null, null, { decodeURIComponent: demoDecode }),
239+
{ aa: 'aa', bb: 'bb', cc: 'cc' });
240+
241+
242+
// Test custom encode
243+
function demoEncode(str) {
244+
return str[0];
245+
}
246+
var obj = { aa: 'aa', bb: 'bb', cc: 'cc' };
247+
assert.equal(
248+
qs.stringify(obj, null, null, { encodeURIComponent: demoEncode }),
249+
'a=a&b=b&c=c');

0 commit comments

Comments
 (0)