如何正確的使用npm qs模塊?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
簡介
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 也可以用[]解析數組:
var withArray = qs.parse('a[]=b&a[]=c'); assert.deepEqual(withArray, { a: ['b', 'c'] });
可以指定數組索引:
var withIndexes = qs.parse('a[1]=c&a[0]=b'); assert.deepEqual(withIndexes, { a: ['b', 'c'] });
請注意,如果想要將字符串解析成數組而不是對象,那么[]之間的值必須是一個數字。 在創建具有特定索引的數組時,qs會將稀疏數組壓縮為僅保留其順序的現有值:
var noSparse = qs.parse('a[1]=b&a[15]=c'); assert.deepEqual(noSparse, { a: ['b', 'c'] });
空字符串也是一個值,并將被保留:
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'] });
qs 還會限制數組最大索引為 20,任何索引大于20的數組成員都將被轉換為以索引為鍵的對象:
var withMaxIndex = qs.parse('a[100]=b'); assert.deepEqual(withMaxIndex, { a: { '100': 'b' } });
arrayLimit 選項可以修改默認限制:
var withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 }); assert.deepEqual(withArrayLimit, { a: { '1': 'b' } });
字符串不解析成數組,可以設置 parseArrays 為 false
var noParsingArrays = qs.parse('a[]=b', { parseArrays: false }); assert.deepEqual(noParsingArrays, { a: { '0': 'b' } });
如果混合使用兩種格式,qs 會將字符串解析為對象:
var mixedNotation = qs.parse('a[0]=b&a[b]=c'); assert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } });
也可以創建元素為對象的數組:
var arraysOfObjects = qs.parse('a[][b]=c'); assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });
序列化字符串
qs.stringify(object, [options]);
默認情況下,對象序列化后進行URI編碼后輸出:
assert.equal(qs.stringify({ a: 'b' }), 'a=b'); assert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
通過設置 encode 為 false 禁止 URI 編碼:
var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false }); assert.equal(unencoded, 'a[b]=c');
通過設置 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');
可以通過設置encoder 選項自定義編碼方式(注意:當 encode 被設置為 false 的時候,不適用):
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) { // Passed in values `a`, `b`, `c` return // Return encoded string }})
與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.
當數組被序列化時,默認顯示索引:
qs.stringify({ a: ['b', 'c', 'd'] }); // 'a[0]=b&a[1]=c&a[2]=d'
可以通過設置 indices 為 false 不顯示索引:
qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }); // 'a=b&a=c&a=d'
可以通過設置 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'
對象序列化時,默認使用 [] 表示法:
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }); // 'a[b][c]=d&a[b][e]=f'
通過設置 allowDots 為 true修改為點表示法:
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true }); // 'a.b.c=d&a.b.e=f'
空字符串和null值將被省略,但是=會保留:
assert.equal(qs.stringify({ a: '' }), 'a=');
沒有值的鍵將什么也不返回(例如空對象或數組):
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: {}} }), '');
值為 undefined 的屬性將會被完全忽略:
assert.equal(qs.stringify({ a: null, b: undefined }), 'a=');
addQueryPrefix 設置為 true可以在查詢字符串前面加 ?:
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');
分隔符也可以設置:
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
如果只是序列化日期對象,可以使用 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' );
可以使用 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');
最后,可以使用 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'
處理 null 值
默認情況下,null 值被視為空對象:
var withNull = qs.stringify({ a: null, b: '' }); assert.equal(withNull, 'a=&b=');
解析字符串的時候并不會區分參數有沒有等號,沒有值的話都會解析為空字符串:
var equalsInsensitive = qs.parse('a&b='); assert.deepEqual(equalsInsensitive, { a: '', b: '' });
要想區分空字符串和null值可以使用 strictNullHandling 選項,序列化后的 null 值沒有=
var strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true }); assert.equal(strictNull, 'a&b=');
要解析不帶 = 的值返回 null可以使用 strictNullHandling 選項:
var parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true }); assert.deepEqual(parsedStrictNull, { a: null, b: '' });
想要完全跳過值為 null 的鍵不解析,可以使用 skipNulls 選項:
var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true }); assert.equal(nullsSkipped, 'a=b');
處理特殊字符集:
默認情況下,字符的編碼和解碼在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');
這也適用于解碼查詢字符串:
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: 'こんにちは!' });
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');
看完上述內容,你們掌握如何正確的使用npm qs模塊的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。