From 4729202d1ef102794616fbc460af6b2ac2a31131 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Tue, 29 Apr 2014 09:40:40 +0800 Subject: [PATCH] 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 --- doc/api/querystring.markdown | 22 +++++++++++++++++++++- lib/querystring.js | 23 ++++++++++++++++------- test/simple/test-querystring.js | 19 +++++++++++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/doc/api/querystring.markdown b/doc/api/querystring.markdown index 2d5613a..67f7e45 100644 --- a/doc/api/querystring.markdown +++ b/doc/api/querystring.markdown @@ -7,12 +7,15 @@ This module provides utilities for dealing with query strings. It provides the following methods: -## querystring.stringify(obj, [sep], [eq]) +## querystring.stringify(obj, [sep], [eq], [options]) Serialize an object to a query string. Optionally override the default separator (`'&'`) and assignment (`'='`) characters. +Options object may contain `encodeURIComponent` property (`querystring.escape` by default), +it can be used to encode string with `non-utf8` encoding if necessary. + Example: querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' }) @@ -23,6 +26,13 @@ Example: // returns 'foo:bar;baz:qux' + // Suppose gbkEncodeURIComponent function already exists, + // it can encode string with `gbk` encoding + querystring.stringify({ w: '中文', foo: 'bar' }, null, null, + { encodeURIComponent: gbkEncodeURIComponent }) + // returns + 'w=%D6%D0%CE%C4&foo=bar' + ## querystring.parse(str, [sep], [eq], [options]) Deserialize a query string to an object. @@ -32,12 +42,22 @@ characters. Options object may contain `maxKeys` property (equal to 1000 by default), it'll be used to limit processed keys. Set it to 0 to remove key count limitation. +Options object may contain `decodeURIComponent` property (`decodeURIComponent` by default), +it can be used to decode `non-utf8` encoding string if necessary. + Example: querystring.parse('foo=bar&baz=qux&baz=quux&corge') // returns { foo: 'bar', baz: ['qux', 'quux'], corge: '' } + // Suppose gbkDecodeURIComponent function already exists, + // it can decode `gbk` encoding string + querystring.parse('w=%D6%D0%CE%C4&foo=bar', null, null, + { decodeURIComponent: gbkDecodeURIComponent }) + // returns + { w: '中文', foo: 'bar' } + ## querystring.escape The escape function used by `querystring.stringify`, diff --git a/lib/querystring.js b/lib/querystring.js index 436789e..aa3f3c7 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -125,25 +125,29 @@ var stringifyPrimitive = function(v) { }; -QueryString.stringify = QueryString.encode = function(obj, sep, eq) { +QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) { sep = sep || '&'; eq = eq || '='; if (util.isNull(obj)) { obj = undefined; } + var encode = QueryString.escape; + if (options && typeof options.encodeURIComponent === 'function') { + encode = options.encodeURIComponent; + } + if (util.isObject(obj)) { return Object.keys(obj).map(function(k) { - var ks = QueryString.escape(stringifyPrimitive(k)) + eq; + var ks = encode(stringifyPrimitive(k)) + eq; if (util.isArray(obj[k])) { return obj[k].map(function(v) { - return ks + QueryString.escape(stringifyPrimitive(v)); + return ks + encode(stringifyPrimitive(v)); }).join(sep); } else { - return ks + QueryString.escape(stringifyPrimitive(obj[k])); + return ks + encode(stringifyPrimitive(obj[k])); } }).join(sep); - } return ''; }; @@ -172,6 +176,11 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { len = maxKeys; } + var decode = decodeURIComponent; + if (options && typeof options.decodeURIComponent === 'function') { + decode = options.decodeURIComponent; + } + for (var i = 0; i < len; ++i) { var x = qs[i].replace(regexp, '%20'), idx = x.indexOf(eq), @@ -186,8 +195,8 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { } try { - k = decodeURIComponent(kstr); - v = decodeURIComponent(vstr); + k = decode(kstr); + v = decode(vstr); } catch (e) { k = QueryString.unescape(kstr, true); v = QueryString.unescape(vstr, true); diff --git a/test/simple/test-querystring.js b/test/simple/test-querystring.js index 04d085b..3cee808 100644 --- a/test/simple/test-querystring.js +++ b/test/simple/test-querystring.js @@ -228,3 +228,22 @@ assert.equal(0xeb, b[16]); assert.equal(0xd8, b[17]); assert.equal(0xa2, b[18]); assert.equal(0xe6, b[19]); + + +// Test custom decode +function demoDecode(str) { + return str + str; +} +assert.deepEqual( + qs.parse('a=a&b=b&c=c', null, null, { decodeURIComponent: demoDecode }), + { aa: 'aa', bb: 'bb', cc: 'cc' }); + + +// Test custom encode +function demoEncode(str) { + return str[0]; +} +var obj = { aa: 'aa', bb: 'bb', cc: 'cc' }; +assert.equal( + qs.stringify(obj, null, null, { encodeURIComponent: demoEncode }), + 'a=a&b=b&c=c'); -- 2.7.4