# qs
[][package-url]
[][actions-url]
[][codecov-url]
[][license-url]
[][downloads-url]
[](https://bestpractices.coreinfrastructure.org/projects/9058)
[][package-url]
一个增加了部分安全性的 querystring 解析和字符串化库。
主要维护者:[Jordan Harband](https://github.com/ljharb)
**qs** 模块最初由 [TJ Holowaychuk](https://github.com/visionmedia/node-querystring) 创建和维护。
## 用法
```
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');
```
### 解析对象
[](#preventEval)
```
qs.parse(string, [options]);
```
**qs** 允许你通过在子键名称周围加上方括号 `[]`,在 query string 中创建嵌套对象。
例如,字符串 `'foo[bar]=baz'` 会转换为:
```
assert.deepEqual(qs.parse('foo[bar]=baz'), {
foo: {
bar: 'baz'
}
});
```
当使用 `plainObjects` 选项时,解析后的值会作为 null 对象返回(通过 `{ __proto__: null }` 创建),因此你需要注意它上面不会存在 prototype 方法,并且用户可以将这些名称设置为他们喜欢的任何值:
```
var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });
assert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } });
```
默认情况下,会忽略那些会覆盖对象 prototype 属性的参数。如果你想保留这些字段的数据,可以使用上面提到的 `plainObjects`,或者将 `allowPrototypes` 设置为 `true`,这将允许用户输入覆盖这些属性。
*警告* 启用此选项通常不是一个好主意,因为在尝试使用被覆盖的属性时可能会导致问题。
使用此选项时请务必小心。
```
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** 最多只会解析到 5 层深度。
这意味着如果你尝试解析像 `'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(string, [options])` 传递 `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' } } });
```
你可以使用 `strictDepth` 选项(默认为 false)配置 **qs** 在解析超过此深度的嵌套输入时抛出错误:
```
try {
qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1, strictDepth: true });
} catch (err) {
assert(err instanceof RangeError);
assert.strictEqual(err.message, 'Input depth exceeded depth option of 1 and strictDepth is true');
}
```
深度限制有助于减轻 **qs** 被用于解析用户输入时的滥用行为,建议将其保持为一个合理较小的数字。`strictDepth` 选项通过在超过限制时抛出错误提供了一层保护,使你能够捕获并处理此类情况。
出于类似的原因,默认情况下 **qs** 最多只会解析 1000 个参数。可以通过传递 `parameterLimit` 选项来覆盖此设置:
```
var limited = qs.parse('a=b&c=d', { parameterLimit: 1 });
assert.deepEqual(limited, { a: 'b' });
```
如果你希望在超过限制(例如 `parameterLimit`、`arrayLimit`)时抛出错误,请将 `throwOnLimitExceeded` 选项设置为 `true`。如果 query string 超过了配置的限制,此选项将生成一个描述性错误。
```
try {
qs.parse('a=1&b=2&c=3&d=4', { parameterLimit: 3, throwOnLimitExceeded: true });
} catch (err) {
assert(err instanceof Error);
assert.strictEqual(err.message, 'Parameter limit exceeded. Only 3 parameters allowed.');
}
```
当 `throwOnLimitExceeded` 设置为 `false`(默认)时,**qs** 会解析到指定的 `parameterLimit` 为止,并在不抛出错误的情况下忽略其余部分。
要忽略前导问号,请使用 `ignoreQueryPrefix`:
```
var prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true });
assert.deepEqual(prefixed, { a: 'b', c: 'd' });
```
也可以传递一个可选的分隔符:
```
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' } });
```
可以使用 `decodeDotInKeys` 选项来解码键中的点
注意:它隐含了 `allowDots`,因此如果你将 `decodeDotInKeys` 设置为 `true`,而将 `allowDots` 设置为 `false`,`parse` 将会报错。
```
var withDots = qs.parse('name%252Eobj.first=John&name%252Eobj.last=Doe', { decodeDotInKeys: true });
assert.deepEqual(withDots, { 'name.obj': { first: 'John', last: 'Doe' }});
```
可以使用 `allowEmptyArrays` 选项来允许对象中的空数组值
```
var withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
assert.deepEqual(withEmptyArrays, { foo: [], bar: 'baz' });
```
可以使用 `duplicates` 选项来更改遇到重复键时的行为
```
assert.deepEqual(qs.parse('foo=bar&foo=baz'), { foo: ['bar', 'baz'] });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'combine' }), { foo: ['bar', 'baz'] });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'first' }), { foo: 'bar' });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'last' }), { foo: 'baz' });
```
请注意,带有方括号表示法 (`[]`) 的键总是会合并到数组中,而不管 `duplicates` 的设置如何:
```
assert.deepEqual(qs.parse('a=1&a=2&b[]=1&b[]=2', { duplicates: 'last' }), { a: '2', b: ['1', '2'] });
```
如果你必须处理旧版浏览器或服务,还支持将百分号编码的八位字节解码为 iso-8859-1:
```
var oldCharset = qs.parse('a=%A7', { charset: 'iso-8859-1' });
assert.deepEqual(oldCharset, { a: '§' });
```
一些服务会在表单中添加一个初始的 `utf8=✓` 值,以便旧版本的 Internet Explorer 更有可能以 utf-8 编码提交表单。
此外,服务器可以根据勾号字符的错误编码检查该值,并检测 query string 或 `application/x-www-form-urlencoded` body 是否*不是*以 utf-8 发送的(例如,如果表单具有 `accept-charset` 参数,或者包含它的页面具有不同的字符集)。
**qs** 通过 `charsetSentinel` 选项支持此机制。
如果指定了该选项,返回的对象中将省略 `utf8` 参数。
它将根据勾号的编码方式用于切换到 `iso-8859-1`/`utf-8` 模式。
**重要**:当你同时指定 `charset` 选项和 `charsetSentinel` 选项时,如果请求包含可以从中推断出实际字符集的 `utf8` 参数,`charset` 将被覆盖。
从这个意义上说,`charset` 将作为默认字符集而不是权威字符集。
```
var detectedAsUtf8 = qs.parse('utf8=%E2%9C%93&a=%C3%B8', {
charset: 'iso-8859-1',
charsetSentinel: true
});
assert.deepEqual(detectedAsUtf8, { a: 'ø' });
// Browsers encode the checkmark as ✓ when submitting as iso-8859-1:
var detectedAsIso8859_1 = qs.parse('utf8=%26%2310003%3B&a=%F8', {
charset: 'utf-8',
charsetSentinel: true
});
assert.deepEqual(detectedAsIso8859_1, { a: 'ø' });
```
如果你想将 `...;` 语法解码为实际字符,你也可以指定 `interpretNumericEntities` 选项:
```
var detectedAsIso8859_1 = qs.parse('a=%26%239786%3B', {
charset: 'iso-8859-1',
interpretNumericEntities: true
});
assert.deepEqual(detectedAsIso8859_1, { a: '☺' });
```
当在 `charsetSentinel` 模式下检测到字符集时,它也可以正常工作。
### 解析数组
**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'] });
```
你也可以使用 `allowSparse` 选项来解析稀疏数组:
```
var sparseArray = qs.parse('a[1]=2&a[3]=5', { allowSparse: true });
assert.deepEqual(sparseArray, { a: [, '2', , '5'] });
```
请注意,空字符串也是一个值,并且会被保留:
```
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` 或更大的数组成员都将转换为以索引为键的对象。
这是为了处理某些人发送例如 `a[999999999]` 的情况,遍历这么大的数组将耗费大量时间。
```
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' } });
```
如果你想在超过数组限制时抛出错误,请将 `throwOnLimitExceeded` 选项设置为 `true`。如果 query string 超过了配置的限制,此选项将生成一个描述性错误。
```
try {
qs.parse('a[1]=b', { arrayLimit: 0, throwOnLimitExceeded: true });
} catch (err) {
assert(err instanceof Error);
assert.strictEqual(err.message, 'Array limit exceeded. Only 0 elements allowed in an array.');
}
```
当 `throwOnLimitExceeded` 设置为 `false`(默认)时,**qs** 会解析到指定的 `arrayLimit` 为止,如果超过限制,该数组将转换为以索引为键的对象
要防止数组语法(`a[]`, `a[0]`)被解析为数组,请将 `parseArrays` 设置为 `false`。
请注意,当 `duplicates` 为 `'combine'`(默认值)时,重复键(例如 `a=b&a=c`)仍可能产生数组。
```
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' } });
```
当一个键同时作为普通值和对象出现时,默认情况下 **qs** 会将冲突的值包装在一个数组中(`strictMerge` 默认为 `true`):
```
assert.deepEqual(qs.parse('a[b]=c&a=d'), { a: [{ b: 'c' }, 'd'] });
assert.deepEqual(qs.parse('a=d&a[b]=c'), { a: ['d', { b: 'c' }] });
```
要恢复旧行为(即使用原始值作为键且值为 `true`),请将 `strictMerge` 设置为 `false`:
```
assert.deepEqual(qs.parse('a[b]=c&a=d', { strictMerge: false }), { a: { b: 'c', d: true } });
```
你也可以创建对象数组:
```
var arraysOfObjects = qs.parse('a[][b]=c');
assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });
```
有些人使用逗号来连接数组,**qs** 可以解析它:
```
var arraysOfObjects = qs.parse('a=b,c', { comma: true })
assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
```
(_这无法转换嵌套对象,例如 `a={b:1},{c:d}`_)
### 解析原始/标量值(数字、布尔值、null 等)
默认情况下,所有值都被解析为字符串。
此行为不会改变,并在 [issue #91](https://github.com/ljharb/qs/issues/91) 中进行了解释。
```
var primitiveValues = qs.parse('a=15&b=true&c=null');
assert.deepEqual(primitiveValues, { a: '15', b: 'true', c: 'null' });
```
如果你希望将看起来像数字、布尔值和其他值的值自动转换为其对应的原始类型,你可以使用 [query-types Express JS 中间件](https://github.com/xpepermint/query-types),它会自动转换所有请求的 query 参数。
### 字符串化
[](#preventEval)
```
qs.stringify(object, [options]);
```
字符串化时,**qs** 默认会对输出进行 URI 编码。对象会按你的预期进行字符串化:
```
assert.equal(qs.stringify({ a: 'b' }), 'a=b');
assert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
```
可以通过将 `encode` 选项设置为 `false` 来禁用此编码:
```
var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });
assert.equal(unencoded, 'a[b]=c');
```
可以通过将 `encodeValuesOnly` 选项设置为 `true` 来仅对值禁用编码:
```
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` 选项的自定义编码方法来替换:
```
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) {
// Passed in values `a`, `b`, `c`
return // Return encoded string
}})
```
_(注意:如果 `encode` 为 `false`,则 `encoder` 选项不适用)_
与 `encoder` 类似,`parse` 也有一个 `decoder` 选项,用于覆盖属性和值的解码:
```
var decoded = qs.parse('x=z', { decoder: function (str) {
// Passed in values `x`, `z`
return // Return decoded string
}})
```
你可以通过使用提供给 encoder 的 type 参数,使用不同的逻辑对键和值进行编码:
```
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultEncoder, charset, type) {
if (type === 'key') {
return // Encoded key
} else if (type === 'value') {
return // Encoded value
}
}})
```
type 参数也会提供给 decoder:
```
var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset, type) {
if (type === 'key') {
return // Decoded key
} else if (type === 'value') {
return // Decoded value
}
}})
```
为了清晰起见,之后的示例将假定输出未进行 URI 编码。
请注意,在实际使用中,这些情况下的返回值*将会*进行 URI 编码。
对数组进行字符串化时,它们遵循 `arrayFormat` 选项,该选项默认为 `indices`:
```
qs.stringify({ a: ['b', 'c', 'd'] });
// 'a[0]=b&a[1]=c&a[2]=d'
```
你可以通过将 `indices` 选项设置为 `false` 来覆盖此设置,或者更明确地将 `arrayFormat` 选项设置为 `repeat`:
```
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'] }, { arrayFormat: 'comma' })
// 'a=b,c'
```
注意:当 `arrayFormat` 设置为 `'comma'` 时,你还可以将 `commaRoundTrip` 选项设置为 `true` 或 `false`,以便在单元素数组上附加 `[]`,从而使它们可以通过 parse 进行往返转换。
对对象进行字符串化时,默认使用方括号表示法:
```
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'
```
你可以通过将 `encodeDotInKeys` 选项设置为 `true` 来对对象键中的点表示法进行编码:
注意:它隐含了 `allowDots`,因此如果你将 `decodeDotInKeys` 设置为 `true`,而将 `allowDots` 设置为 `false`,`stringify` 将会报错。
注意事项:当 `encodeValuesOnly` 为 `true` 且 `encodeDotInKeys` 也为 `true` 时,只会对键中的点进行编码,其他内容则不会。
```
qs.stringify({ "name.obj": { "first": "John", "last": "Doe" } }, { allowDots: true, encodeDotInKeys: true })
// 'name%252Eobj.first=John&name%252Eobj.last=Doe'
```
你可以通过将 `allowEmptyArrays` 选项设置为 `true` 来允许空数组值:
```
qs.stringify({ foo: [], bar: 'baz' }, { allowEmptyArrays: true });
// 'foo[]&bar=baz'
```
空字符串和 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=');
```
query string 可以选择性地加上问号前缀:
```
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');
```
请注意,当输出为空字符串时,不会添加前缀:
```
assert.equal(qs.stringify({}, { addQueryPrefix: true }), '');
```
在 stringify 中也可以覆盖分隔符:
```
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
```
如果你只想覆盖 `Date` 对象的序列化,你可以提供一个 `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` 选项来限制字符串化输出中包含哪些键。
如果你传递一个函数,它将为每个键调用以获取替换值。
否则,如果你传递一个数组,它将用于选择要进行字符串化的属性和数组索引:
```
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'
```
你也可以使用 `filter` 为用户自定义类型注入自定义序列化。
假设你正在使用某个期望范围格式 query string 的 API:
```
https://domain.com/endpoint?range=30...70
```
你将其建模为:
```
class Range {
constructor(from, to) {
this.from = from;
this.to = to;
}
}
```
你可以_注入_一个自定义序列化器来处理这种类型的值:
```
qs.stringify(
{
range: new Range(30, 70),
},
{
filter: (prefix, value) => {
if (value instanceof Range) {
return `${value.from}...${value.to}`;
}
// serialize the usual way
return value;
},
}
);
// range=30...70
```
### 处理 `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` 值的键,请使用 `skips` 标志:
```
var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });
assert.equal(nullsSkipped, 'a=b');
```
如果你在与遗留系统通信,你可以使用 `charset` 选项切换到 `iso-8859-1`:
```
var iso = qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' });
assert.equal(iso, '%E6=%E6');
```
`iso-8859-1` 中不存在的字符将被转换为数字实体,类似于浏览器的做法:
```
var numeric = qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' });
assert.equal(numeric, 'a=%26%239786%3B');
```
你可以使用 `charsetSentinel` 选项,通过包含一个带有正确编码勾号的 `utf8=✓` 参数来声明字符,类似于 Ruby on Rails 等在提交表单时的做法。
```
var sentinel = qs.stringify({ a: '☺' }, { charsetSentinel: true });
assert.equal(sentinel, 'utf8=%E2%9C%93&a=%E2%98%BA');
var isoSentinel = qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' });
assert.equal(isoSentinel, 'utf8=%26%2310003%3B&a=%E6');
```
### 处理特殊字符集
默认情况下,字符的编码和解码是在 `utf-8` 中完成的,并且通过 `charset` 参数也内置了对 `iso-8859-1` 的支持。
如果你希望将 querystring 编码为不同的字符集(例如
[Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)),你可以使用
[`qs-iconv`](https://github.com/martinheidegger/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');
```
这也适用于 query string 的解码:
```
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 和 RFC 1738 空格编码
RFC3986 用作默认选项,并将 ' ' 编码为 *%20*,这是向后兼容的。
同时,根据 RFC1738,输出可以被字符串化,其中 ' ' 等于 '+'。
```
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');
```
## 安全
如果你有潜在的安全漏洞需要报告,请发送电子邮件给 [@ljharb](https://github.com/ljharb) 或访问 https://tidelift.com/security。
## 面向企业的 qs
作为 Tidelift 订阅的一部分提供
qs 以及数千个其他包的维护者正在与 Tidelift 合作,为你用于构建应用程序的开源依赖项提供商业支持和维护。
节省时间,降低风险,改善代码健康状况,同时为你所使用的确切依赖项的维护者提供报酬。
[了解更多。](https://tidelift.com/subscription/pkg/npm-qs?utm_source=npm-qs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
## 鸣谢
qs logo 由 [NUMI](https://github.com/numi-hq/open-design) 提供:
[

](https://numi.tech/?ref=qs)