neoqs
一个具有额外安全功能的查询字符串解析和字符串化库。它是 qs 的一个分支和 TypeScript 重写版本,旨在成为现代、轻量级但完全向后兼容 qs
的库。
主要维护者:Puru Vijay
qs 模块最初由 TJ Holowaychuk 创建和维护。
neoqs
未得到 qs
当前维护者的认可。
- 🤌 3.9KB min+brotli(比
qs
小 3 倍) - 🚥 零依赖
- 🎹 TypeScript。摒弃
@types/qs
包 - ❎ 无 polyfills
- 🛸 ESM 优先
- 📜 支持 ES5 的传统模式
本包旨在遵循以下规则,并将长期坚持:
- 无依赖。
- 无 polyfills。
- ESM 优先。
- 力求现代化
- 始终提供传统模式
- 始终遵循
qs
API。已经有许多包做到了这一点。neoqs
旨在成为qs
的直接替代品,提供相同的 API,但零依赖并改善开发者体验。
何时使用本包?
本包旨在成为 qs
的直接替代品,提供相同的 API,但零依赖并改善开发者体验。因此,如果你的项目中已经在使用 qs
,你应该使用本包来替代。
何时不使用本包?
如果你的用例非常简单(foo=bar&baz=baka
),主要是顶层键(foo=bar
),且不需要支持非常旧的浏览器和 Node 版本,那么从你的项目中删除 qs
和 neoqs
,改用 URLSearchParams。
使用哪个构建版本?
neoqs
提供两个构建版本:
- 默认版:向后兼容
qs
并提供相同的 API,但仅支持 ESM,编译为 ES2022,适用于 Node 18+ - 传统版:ES5 和 CommonJS 的传统构建,兼容
qs
并提供相同的 API。理论上可以支持到 Node 4.0.0,但未经测试。
以下是不同构建版本的对比矩阵:
构建版本 | ESM | CJS | 浏览器 | Node | Polyfills | 大小 |
---|---|---|---|---|---|---|
默认版 | ✅ ES2022 | ✅ | ✅ | ❌ | 3.9KB min+brotli | |
传统版 | ✅ ES5 | ✅ | ✅ | ✅ | ❌ | 4.2KB min+brotli |
如果你:
正在发布一个支持 CommonJS 的库:
使用 传统版
构建以兼容旧浏览器和 Node 版本。
const { parse, stringify } = require('neoqs/legacy');
ESM:
import { parse, stringify } from 'neoqs/legacy';
不关心旧浏览器或 Node 版本:
使用默认构建以避免破坏性变更,并获得更好的开发者体验。
import * as qs from 'neoqs';
const obj = qs.parse('a=c');
console.log(obj);
const str = qs.stringify(obj);
console.log(str);
关心旧浏览器或 Node 或 CommonJS 版本:
使用传统构建以兼容旧浏览器和 Node 版本。
const { parse, stringify } = require('neoqs/legacy');
ESM:
import { parse, stringify } from 'neoqs/legacy';
使用方法
import * as qs from 'neoqs';
const obj = qs.parse('a=c');
console.log(obj);
const str = qs.stringify(obj);
console.log(str);
解析对象
parse(string, [options]);
qs 允许你在查询字符串中创建嵌套对象,方法是用方括号 []
包围子键的名称。例如,字符串 'foo[bar]=baz'
会转换为:
console.log(qs.parse('foo[bar]=baz'));
// 输出:
// {
// foo: {
// bar: 'baz'
// }
//});
当使用 plainObjects
选项时,解析后的值会以空对象的形式返回,通过 Object.create(null)
创建,因此你应该注意,原型方法不会存在于其上,用户可以将这些名称设置为任何他们喜欢的值:
const nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });
console.log(nullObject);
// 输出:
// {
// a: {
// hasOwnProperty: 'b'
// }
// }
默认情况下,会忽略会覆盖对象原型属性的参数。如果你希望保留这些字段的数据,可以使用上面提到的 plainObjects
,或将 allowPrototypes
设置为 true
,这将允许用户输入覆盖这些属性。警告:启用此选项通常是一个坏主意,因为它可能会在尝试使用被覆盖的属性时导致问题。使用此选项时请务必小心。
const protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true });
console.log(protoObject);
// 输出:
// {
// a: {
// hasOwnProperty: 'b'
// }
// }
URI 编码的字符串也可以正常工作:
console.log(qs.parse('a%5Bb%5D=c'));
// 输出:
// {
// a: {
// b: 'c'
// }
// }
你也可以嵌套对象,如 'foo[bar][baz]=foobarbaz'
:
console.log(qs.parse('foo[bar][baz]=foobarbaz'));
// 输出:
// {
// foo: {
// bar: {
// baz: 'foobarbaz'
// }
// }
// }
默认情况下,在嵌套对象时,qs 最多只会解析 5 层深度。这意味着如果你尝试解析像 'a[b][c][d][e][f][g][h][i]=j'
这样的字符串,你得到的对象将是:
const expected = {
a: {
b: {
c: {
d: {
e: {
f: {
'[g][h][i]': 'j'
}
}
}
}
}
}
};
console.log(qs.stringify(expected));
// 输出:
// 'a[b][c][d][e][f][g][h][i]=j'
可以通过向 qs.parse(string, [options])
传递 depth
选项来覆盖这个深度:
const deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
console.log(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
选项来覆盖这个限制:
const limited = qs.parse('a=b&c=d', { parameterLimit: 1 });
console.log(limited);
// 输出:
// {
// a: 'b'
// }
要忽略开头的问号,可以使用 ignoreQueryPrefix
:
const prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true });
console.log(prefixed);
// 输出:
// {
// a: 'b',
// c: 'd'
// }
还可以传递一个可选的分隔符:
const delimited = qs.parse('a=b;c=d', { delimiter: ';' });
console.log(delimited);
// 输出:
// {
// a: 'b',
// c: 'd'
// }
分隔符也可以是正则表达式:
const regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
console.log(regexed);
// 输出:
// {
// a: 'b',
// c: 'd',
// e: 'f'
// }
可以使用 allowDots
选项来启用点号表示法:
const withDots = qs.parse('a.b=c', { allowDots: true });
console.log(withDots);
// 输出:
// {
// a: {
// b: 'c'
// }
// }
可以使用 decodeDotInKeys
选项来解码键中的点号。注意:这隐含了 allowDots
,所以如果你将 decodeDotInKeys
设置为 true
,而 allowDots
设置为 false
,parse
将会报错。
const withDots = qs.parse('name%252Eobj.first=John&name%252Eobj.last=Doe', {
decodeDotInKeys: true
});
console.log(withDots);
// 输出:
// {
// 'name.obj': {
// first: 'John',
// last: 'Doe'
// }
// }
可以使用allowEmptyArrays
选项来允许对象中的空数组值
const withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
console.log(withEmptyArrays);
// 输出:
// {
// foo: [],
// bar: 'baz'
// }
可以使用duplicates
选项来更改遇到重复键时的行为
console.log(qs.parse('foo=bar&foo=baz', { duplicates: 'combine' }));
// 输出:
// {
// foo: ['bar', 'baz']
// }
console.log(qs.parse('foo=bar&foo=baz', { duplicates: 'first' }));
// 输出:
// {
// foo: 'bar'
// }
console.log(qs.parse('foo=bar&foo=baz', { duplicates: 'last' }));
// 输出:
// {
// foo: 'baz'
// }
console.log(qs.parse('foo=bar&foo=baz', { duplicates: 'replace' }));
// 输出:
// {
// foo: 'bar'
// }
如果您需要处理旧版浏览器或服务,还支持将百分比编码的八进制解码为iso-8859-1:
const oldCharset = qs.parse('a=%A7', { charset: 'iso-8859-1' });
console.log(oldCharset);
// 输出:
// {
// a: '§'
// }
一些服务会在表单中添加一个初始的utf8=✓
值,以便旧版Internet Explorer更有可能以utf-8提交表单。此外,服务器可以检查勾选标记字符的错误编码值,并检测查询字符串或application/x-www-form-urlencoded
主体是否未以utf-8发送,例如,如果表单有accept-charset
参数或包含页面有不同的字符集。
qs通过charsetSentinel
选项支持此机制。如果指定,返回对象中将省略utf8
参数。它将用于根据勾选标记的编码方式切换到iso-8859-1
/utf-8
模式。
重要:当您同时指定charset
选项和charsetSentinel
选项时,如果请求包含可以推断实际字符集的utf8
参数,charset
将被覆盖。在这种情况下,charset
将作为默认字符集而不是权威字符集。
const detectedAsUtf8 = qs.parse('utf8=%E2%9C%93&a=%C3%B8', {
charset: 'iso-8859-1',
charsetSentinel: true
});
console.log(detectedAsUtf8);
// 输出:
// {
// a: 'ø'
// }
// 浏览器在以iso-8859-1提交时将勾选标记编码为✓:
const detectedAsIso8859_1 = qs.parse('utf8=%26%2310003%3B&a=%F8', {
charset: 'utf-8',
charsetSentinel: true
});
console.log(detectedAsIso8859_1);
// 输出:
// {
// a: 'ø'
// }
如果您想将&#...;
语法解码为实际字符,您还可以指定interpretNumericEntities
选项:
const detectedAsIso8859_1 = qs.parse('a=%26%239786%3B', {
charset: 'iso-8859-1',
interpretNumericEntities: true
});
console.log(detectedAsIso8859_1);
// 输出: {
// a: '☺'
// }
在charsetSentinel
模式下检测字符集时,它也能正常工作。
解析数组
qs还可以使用类似的[]
表示法解析数组:
const withArray = qs.parse('a[]=b&a[]=c');
console.log(withArray);
// 输出:
// {
// a: ['b', 'c']
// }
您也可以指定索引:
const withIndexes = qs.parse('a[1]=c&a[0]=b');
console.log(withIndexes);
// 输出:
// {
// a: ['b', 'c']
// }
请注意,数组中的索引和对象中的键之间的唯一区别是方括号之间的值必须是数字才能创建数组。在创建具有特定索引的数组时,qs将压缩稀疏数组,只保留现有值并保持它们的顺序:
const noSparse = qs.parse('a[1]=b&a[15]=c');
console.log(noSparse);
// 输出:
// {
// a: ['b', 'c']
// }
您也可以使用allowSparse
选项来解析稀疏数组:
const sparseArray = qs.parse('a[1]=2&a[3]=5', { allowSparse: true });
console.log(sparseArray);
// 输出:
// {
// a: [, '2', , '5']
// }
请注意,空字符串也是一个值,并且会被保留:
const withEmptyString = qs.parse('a[]=&a[]=b');
console.log(withEmptyString);
// 输出:
// {
// a: ['', 'b']
// }
const withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c');
console.log(withIndexedEmptyString);
// 输出:
// {
// a: ['b', '', 'c']
// }
qs还会将数组中指定的索引限制为最大索引20。任何索引大于20的数组成员将转换为以索引为键的对象。这是为了处理有人发送例如a[999999999]
的情况,遍历这个巨大的数组将花费大量时间。
const withMaxIndex = qs.parse('a[100]=b');
console.log(withMaxIndex);
// 输出:
// {
// a: {
// 100: 'b'
// }
// }
可以通过传递arrayLimit
选项来覆盖此限制:
const withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 });
console.log(withArrayLimit);
// 输出:
// {
// a: {
// 1: 'b'
// }
// }
要完全禁用数组解析,请将parseArrays
设置为false
。
const noParsingArrays = qs.parse('a[]=b', { parseArrays: false });
console.log(noParsingArrays);
// 输出:
// {
// a: {
// 0: 'b'
// }
// }
如果您混合使用不同的表示法,qs会将两个项目合并为一个对象:
const mixedNotation = qs.parse('a[0]=b&a[b]=c');
console.log(mixedNotation);
// 输出:
// {
// a: {
// 0: 'b',
// b: 'c'
// }
// }
您还可以创建对象数组:
const arraysOfObjects = qs.parse('a[][b]=c');
console.log(arraysOfObjects);
// 输出:
// {
// a: [{ b: 'c' }]
// }
有些人使用逗号连接数组,qs可以解析它:
const arraysOfObjects = qs.parse('a=b,c', { comma: true });
console.log(arraysOfObjects);
// 输出:
// {
// a: ['b', 'c']
// }
(这无法转换嵌套对象,例如a={b:1},{c:d}
)
解析原始/标量值(数字、布尔值、null等)
默认情况下,所有值都被解析为字符串。这种行为不会改变,并在问题#91中进行了解释。
const primitiveValues = qs.parse('a=15&b=true&c=null');
console.log(primitiveValues);
// 输出:
// {
// a: '15',
// b: 'true',
// c: 'null'
// }
如果您希望自动将看起来像数字、布尔值和其他值的值转换为它们的原始对应项,您可以使用query-types Express JS中间件,它将自动转换所有请求查询参数。
字符串化
qs.stringify(object, [options]);
在字符串化时,qs默认对输出进行URI编码。对象按照您期望的方式进行字符串化:
console.log(qs.stringify({ a: 'b' }));
// 输出:
// a=b
console.log(qs.stringify({ a: { b: 'c' } }));
// 输出:
// a%5Bb%5D=c
可以通过将encode
选项设置为false
来禁用此编码:
const unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });
console.log(unencoded);
// 输出:
// a[b]=c
可以通过将encodeValuesOnly
选项设置为true
来禁用对键的编码:
const encodedValues = qs.stringify(
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
{ encodeValuesOnly: true }
);
console.log(encodedValues);
// 输出:
// a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h
这种编码也可以通过设置encoder
选项为自定义编码方法来替换:
const encoded = qs.stringify(
{ a: { b: 'c' } },
{
encoder: function (str) {
// 传入的值为 `a`、`b`、`c`
return; // 返回编码后的字符串
}
}
);
(注意:如果 encode
为 false
,则 encoder
选项不适用)
与 encoder
类似,parse
方法也有一个 decoder
选项,用于覆盖属性和值的解码:
const decoded = qs.parse('x=z', {
decoder: function (str) {
// 传入的值为 `x`、`z`
return; // 返回解码后的字符串
}
});
你可以使用提供给编码器的类型参数,对键和值使用不同的逻辑进行编码:
const encoded = qs.stringify(
{ a: { b: 'c' } },
{
encoder: function (str, defaultEncoder, charset, type) {
if (type === 'key') {
return; // 编码后的键
} else if (type === 'value') {
return; // 编码后的值
}
}
}
);
解码器也提供了类型参数:
const decoded = qs.parse('x=z', {
decoder: function (str, defaultDecoder, charset, type) {
if (type === 'key') {
return; // 解码后的键
} else if (type === 'value') {
return; // 解码后的值
}
}
});
为了清晰起见,此后的示例将展示未经 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
,以在单项数组上附加 []
,使其能够通过解析进行往返传输。
当对象被字符串化时,默认使用方括号表示法:
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 值将省略值,但等号 (=) 保留:
console.log(qs.stringify({ a: '' }));
// 输出:
// a=
没有值的键(如空对象或数组)将不返回任何内容:
console.log(qs.stringify({ a: [] }));
// 输出:
// ''
console.log(qs.stringify({ a: {} }));
// 输出:
// ''
console.log(qs.stringify({ a: [{}] }));
// 输出:
// ''
console.log(qs.stringify({ a: { b: [] } }));
// 输出:
// ''
console.log(qs.stringify({ a: { b: {} } }));
// 输出:
// ''
设置为 undefined
的属性将被完全省略:
console.log(qs.stringify({ a: null, b: undefined }));
// 输出:
// a=
查询字符串可以选择在前面加上问号:
console.log(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }));
// 输出:
// ?a=b&c=d
分隔符也可以在 stringify 中被覆盖:
console.log(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }));
// 输出:
// a=b;c=d
如果你只想覆盖 Date
对象的序列化,可以提供一个 serializeDate
选项:
const date = new Date(7);
console.log(qs.stringify({ a: date }));
// 输出:
// a=1970-01-01T00:00:00.007Z
console.log(
qs.stringify(
{ a: date },
{
serializeDate: function (d) {
return d.getTime();
}
}
)
);
// 输出:
// a=7
你可以使用 sort
选项来影响参数键的顺序:
function alphabeticalSort(a, b) {
return a.localeCompare(b);
}
console.log(qs.stringify({ a: 'c', z: 'y', b: 'f' }, { sort: alphabeticalSort }));
// 输出:
// a=c&b=f&z=y
最后,你可以使用 filter
选项来限制哪些键将被包含在字符串化的输出中。如果你传递一个函数,它将为每个键调用以获取替换值。否则,如果你传递一个数组,它将用于选择要字符串化的属性和数组索引:
function filterFunc(prefix, value) {
if (prefix == 'b') {
// 返回 `undefined` 值以省略属性。
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
为用户定义的类型注入自定义序列化。假设你正在使用一个 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}`;
}
// 以常规方式序列化
return value;
}
}
);
// range=30...70
处理 null
值
默认情况下,null
值被视为空字符串:
const withNull = qs.stringify({ a: null, b: '' });
console.log(withNull);
// 输出:
// a=&b=1
解析时不区分有等号和无等号的参数。两者都被转换为空字符串。
const equalsInsensitive = qs.parse('a&b=');
console.log(equalsInsensitive);
// 输出:
// {
// a: '',
// b: ''
// }
要区分 null
值和空字符串,请使用 strictNullHandling
标志。在结果字符串中,null
值没有 =
符号:
const strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
console.log(strictNull);
// 输出:
// a&b=
要将没有=
的值解析回null
,请使用strictNullHandling
标志:
const parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true });
console.log(parsedStrictNull);
// 输出:
// {
// a: null,
// b: ''
// }
要完全跳过渲染值为null
的键,请使用skipNulls
标志:
const nullsSkipped = qs.stringify({ a: 'b', c: null }, { skipNulls: true });
console.log(nullsSkipped);
// 输出:
// a=b
如果您正在与遗留系统通信,可以使用charset
选项切换到iso-8859-1
:
const iso = qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' });
console.log(iso);
// 输出:
// %E6=%E6
在iso-8859-1
中不存在的字符将被转换为数字实体,类似于浏览器的处理方式:
const numeric = qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' });
console.log(numeric);
// 输出:
// a=%26%239786%3B
您可以使用charsetSentinel
选项来通过包含一个utf8=✓
参数来声明字符集,该参数使用正确的编码方式编码复选标记,类似于Ruby on Rails和其他框架在提交表单时的做法。
const sentinel = qs.stringify({ a: '☺' }, { charsetSentinel: true });
console.log(sentinel);
// 输出:
// utf8=%E2%9C%93&a=%E2%98%BA
const isoSentinel = qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' });
console.log(isoSentinel);
// 输出:
// utf8=%26%2310003%3B&a=%E6
处理特殊字符集
默认情况下,字符的编码和解码是以utf-8
进行的,通过charset
参数还内置了对iso-8859-1
的支持。
如果您希望将查询字符串编码为不同的字符集(例如Shift JIS),可以使用qs-iconv
库:
import qsEnvEncoder from 'qs-iconv/encoder';
const encoder = qsEnvEncoder('shift_jis');
const shiftJISEncoded = qs.stringify({ a: 'こんにちは!' }, { encoder: encoder });
console.log(shiftJISEncoded);
// 输出:
// a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I
这也适用于查询字符串的解码:
import qsEnvDecoder from 'qs-iconv/decoder';
const decoder = qsEnvDecoder('shift_jis');
const obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder });
console.log(obj);
// 输出:
// {
// a: 'こんにちは!'
// }
RFC 3986和RFC 1738空格编码
RFC3986作为默认选项使用,将' '编码为_%20_,这是向后兼容的。同时,输出可以根据RFC1738进行字符串化,其中' '等同于'+'。
console.log(qs.stringify({ a: 'b c' }));
// 输出:
// a=b%20c
console.log(qs.stringify({ a: 'b c' }, { format: 'RFC3986' }));
// 输出:
// a=b%20c
console.log(qs.stringify({ a: 'b c' }, { format: 'RFC1738' }));
// 输出:
// a=b+c
安全性
如果您发现潜在的安全漏洞需要报告,请通过Twitter私信联系@puruvjdev。