本文基本使用谷歌翻譯加上自己的理解,權當加深記憶。
簡介
qs 是一個增加了一些安全性的查詢字串解析和序列化字串的庫。
主要維護者:Jordan Harband
最初建立者和維護者:TJ Holowaychuk
用法
var qs = require('qs'); var assert = require('assert'); var obj = qs.parse('a=c'); assert.deepEqual(obj, { a: 'c' }); var str = qs.stringify(obj); assert.equal(str, 'a=c');
解析物件
qs.parse(string, [options]);
qs 允許在查詢字串中使用[]
的方式建立巢狀的物件。例如,字串'foo[bar]=baz'
可以轉換為:
assert.deepEqual(qs.parse('foo[bar]=baz'), { foo: { bar: 'baz' } });
When using the plainObjects option the parsed value is returned as a null object, created via Object.create(null) and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like:
var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true }); assert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } });
By default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use plainObjects as mentioned above, or set allowPrototypes to true which will allow user input to overwrite those properties. WARNING It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option.
var protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true }); assert.deepEqual(protoObject, { a: { hasOwnProperty: 'b' } });
也可以解析 URI 編碼:
assert.deepEqual(qs.parse('a%5Bb%5D=c'), { a: { b: 'c' } });
還可以像這樣巢狀物件:'foo[bar][baz]=foobarbaz'
:
assert.deepEqual(qs.parse('foo[bar][baz]=foobarbaz'), { foo: { bar: { baz: 'foobarbaz' } } });
當使用巢狀物件時,qs 在預設情況下最多解析到的深度是第五層(注:從第一個方括號到算起,到第五個方括號),例如嘗試解析一個這樣的字串'a[b][c][d][e][f][g][h][i]=j'
將會得到以下結果:
var expected = { a: { b: { c: { d: { e: { f: { '[g][h][i]': 'j' } } } } } } }; var string = 'a[b][c][d][e][f][g][h][i]=j'; assert.deepEqual(qs.parse(string), expected);
可以給 qs.parse 傳遞一個 depth
引數覆蓋預設值:
var deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 }); assert.deepEqual(deep, { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } });
-
當 qs 用於解析使用者輸入的時候,解析深度的限制有助於減輕使用者的濫用行為。最好將 depth 設定為一個合理的儘量小的數字。
-
出於類似的原因,qs 在預設情況下最多解析 1000 個引數。通過傳遞
parameterLimit
引數可以修改預設值: -
var limited = qs.parse('a=b&c=d', { parameterLimit: 1 }); assert.deepEqual(limited, { a: 'b' });
忽略查詢字串開頭的
?
可以使用ignoreQueryPrefix
:var prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true }); assert.deepEqual(prefixed, { a: 'b', c: 'd' });
還可以根據自定義的分隔符來解析
delimiter
:var delimited = qs.parse('a=b;c=d', { delimiter: ';' }); assert.deepEqual(delimited, { a: 'b', c: 'd' });
分隔符可以是正規表示式:
var regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ }); assert.deepEqual(regexed, { a: 'b', c: 'd', e: 'f' });
allowDots
選項可以啟用點表示法:var withDots = qs.parse('a.b=c', { allowDots: true }); assert.deepEqual(withDots, { a: { b: 'c' } });
- 解析陣列
- qs 也可以用
[]
解析陣列:-
qs 也可以用
[]
解析陣列:var withArray = qs.parse('a[]=b&a[]=c'); assert.deepEqual(withArray, { a: ['b', 'c'] });
- 1
- 2
-
可以指定陣列索引:
var withIndexes = qs.parse('a[1]=c&a[0]=b'); assert.deepEqual(withIndexes, { a: ['b', 'c'] });
- 1
- 2
-
請注意,如果想要將字串解析成陣列而不是物件,那麼
[]
之間的值必須是一個數字。 在建立具有特定索引的陣列時,qs會將稀疏陣列壓縮為僅保留其順序的現有值:var noSparse = qs.parse('a[1]=b&a[15]=c'); assert.deepEqual(noSparse, { a: ['b', 'c'] });
- 1
- 2
-
空字串也是一個值,並將被保留:
var withEmptyString = qs.parse('a[]=&a[]=b'); assert.deepEqual(withEmptyString, { a: ['', 'b'] }); var withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c'); assert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] });
- 1
- 2
- 3
- 4
- 5
-
qs 還會限制陣列最大索引為 20,任何索引大於20的陣列成員都將被轉換為以索引為鍵的物件:
var withMaxIndex = qs.parse('a[100]=b'); assert.deepEqual(withMaxIndex, { a: { '100': 'b' } });
- 1
- 2
-
arrayLimit
選項可以修改預設限制:var withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 }); assert.deepEqual(withArrayLimit, { a: { '1': 'b' } });
- 1
- 2
-
字串不解析成陣列,可以設定
parseArrays
為false
var noParsingArrays = qs.parse('a[]=b', { parseArrays: false }); assert.deepEqual(noParsingArrays, { a: { '0': 'b' } });
- 1
- 2
-
如果混合使用兩種格式,qs 會將字串解析為物件:
var mixedNotation = qs.parse('a[0]=b&a[b]=c'); assert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } });
- 1
- 2
-
也可以建立元素為物件的陣列:
var arraysOfObjects = qs.parse('a[][b]=c'); assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });
- 1
- 2
-
序列化字串
qs.stringify(object, [options]);
- 1
-
預設情況下,物件序列化後進行URI編碼後輸出:
assert.equal(qs.stringify({ a: 'b' }), 'a=b'); assert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
- 1
- 2
-
通過設定
encode
為false
禁止 URI 編碼:var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false }); assert.equal(unencoded, 'a[b]=c');
- 1
- 2
-
通過設定
encodeValuesOnly
為true
,可以禁用對 key 進行URI 編碼:var encodedValues = qs.stringify( { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, { encodeValuesOnly: true } ); assert.equal(encodedValues,'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h');
- 1
- 2
- 3
- 4
- 5
-
可以通過設定
encoder
選項自定義編碼方式(注意:當encode
被設定為false
的時候,不適用):var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) { // Passed in values `a`, `b`, `c` return // Return encoded string }})
- 1
- 2
- 3
- 4
-
與
encoder
類似decoder
可以用來解碼:var decoded = qs.parse('x=z', { decoder: function (str) { // Passed in values `x`, `z` return // Return decoded string }}) Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases will be URI encoded during real usage.
- 1
- 2
- 3
- 4
- 5
- 6
-
當陣列被序列化時,預設顯示索引:
qs.stringify({ a: ['b', 'c', 'd'] }); // 'a[0]=b&a[1]=c&a[2]=d'
- 1
- 2
-
可以通過設定
indices
為false
不顯示索引:qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }); // 'a=b&a=c&a=d'
- 1
- 2
-
可以通過設定
arrayFormat
選項指定陣列輸出格式:qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }) // 'a[0]=b&a[1]=c' qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }) // 'a[]=b&a[]=c' qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }) // 'a=b&a=c'
- 1
- 2
- 3
- 4
- 5
- 6
-
物件序列化時,預設使用
[]
表示法:qs.stringify({ a: { b: { c: 'd', e: 'f' } } }); // 'a[b][c]=d&a[b][e]=f'
- 1
- 2
-
通過設定
allowDots
為true
修改為點表示法:qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true }); // 'a.b.c=d&a.b.e=f'
- 1
- 2
-
空字串和
null
值將被省略,但是=
會保留:assert.equal(qs.stringify({ a: '' }), 'a=');
- 1
-
沒有值的鍵將什麼也不返回(例如空物件或陣列):
assert.equal(qs.stringify({ a: [] }), ''); assert.equal(qs.stringify({ a: {} }), ''); assert.equal(qs.stringify({ a: [{}] }), ''); assert.equal(qs.stringify({ a: { b: []} }), ''); assert.equal(qs.stringify({ a: { b: {}} }), '');
- 1
- 2
- 3
- 4
- 5
-
值為
undefined
的屬性將會被完全忽略:assert.equal(qs.stringify({ a: null, b: undefined }), 'a=');
- 1
-
addQueryPrefix
設定為true
可以在查詢字串前面加?
:assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');
- 1
-
分隔符也可以設定:
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
- 1
-
如果只是序列化日期物件,可以使用
serializeDate
選項:var date = new Date(7); assert.equal(qs.stringify({ a: date }), 'a=1970-01-01T00:00:00.007Z'.replace(/:/g, '%3A')); assert.equal( qs.stringify({ a: date }, { serializeDate: function (d) { return d.getTime(); } }), 'a=7' );
- 1
- 2
- 3
- 4
- 5
- 6
-
可以使用
sort
選項來修改鍵的順序:function alphabeticalSort(a, b) { return a.localeCompare(b); } assert.equal(qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort }), 'a=c&b=f&z=y');
- 1
- 2
- 3
- 4
-
最後,可以使用
filter
選項過濾序列化輸出的鍵。如果給filter
傳遞一個函式,每個鍵呼叫一次該函式並用返回的值替換原來值。如果給filter
傳遞一個陣列,它將用於選擇物件的key和陣列的索引:function filterFunc(prefix, value) { if (prefix == 'b') { // Return an `undefined` value to omit a property. return; } if (prefix == 'e[f]') { return value.getTime(); } if (prefix == 'e[g][0]') { return value * 2; } return value; } qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc }); // 'a=b&c=d&e[f]=123&e[g][0]=4' qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] }); // 'a=b&e=f' qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] }); // 'a[0]=b&a[2]=d'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 處理
null
值
-
預設情況下,
null
值被視為空物件:var withNull = qs.stringify({ a: null, b: '' }); assert.equal(withNull, 'a=&b=');
- 1
- 2
-
解析字串的時候並不會區分引數有沒有等號,沒有值的話都會解析為空字串:
var equalsInsensitive = qs.parse('a&b='); assert.deepEqual(equalsInsensitive, { a: '', b: '' });
- 1
- 2
-
要想區分空字串和
null
值可以使用strictNullHandling
選項,序列化後的null
值沒有=
var strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true }); assert.equal(strictNull, 'a&b=');
- 1
- 2
-
要解析不帶
=
的值返回null
可以使用strictNullHandling
選項:var parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true }); assert.deepEqual(parsedStrictNull, { a: null, b: '' });
- 1
- 2
-
想要完全跳過值為
null
的鍵不解析,可以使用skipNulls
選項:var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true }); assert.equal(nullsSkipped, 'a=b');
- 1
- 2
- 處理特殊字符集:
-
預設情況下,字元的編碼和解碼在utf-8中完成。 如果希望將查詢字串編碼為不同的字符集(i.e.Shift JIS),您可以使用qs-iconv庫:
var encoder = require('qs-iconv/encoder')('shift_jis'); var shiftJISEncoded = qs.stringify({ a: 'こんにちは!' }, { encoder: encoder }); assert.equal(shiftJISEncoded, 'a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I');
- 1
- 2
- 3
-
這也適用於解碼查詢字串:
var decoder = require('qs-iconv/decoder')('shift_jis'); var obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder }); assert.deepEqual(obj, { a: 'こんにちは!' });
- 1
- 2
- 3
-
RFC 3986 and RFC 1738 space encoding
RFC3986 used as default option and encodes ’ ’ to %20 which is backward compatible. In the same time, output can be stringified as per RFC1738 with ’ ’ equal to ‘+’.
assert.equal(qs.stringify({ a: 'b c' }), 'a=b%20c'); assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c'); assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');
-