@d-fischer/qs

中文文档
7.0.2发布于 2020/04/03BSD-3-ClauseTypeScriptESM + CJS零依赖近 30 天下载 2.2万

一个支持嵌入和阵列的查询字符串解析器,其深度限制

中文使用文档
文档目录9 节
  1. 使用量
  2. 分析对象
  3. 分析阵列
  4. 字符串
  5. 处理 null 数值
  6. 处理特殊字符集
  7. RFC 3986和RFC 1738空间编码
  8. 警卫
  9. qs 企业

数量 Version Badge

Build Status dependency status dev dependency status License Downloads

npm badge

一个查询字符串解析和字符串化库,并附加一些安全性.

主维护者 : 约旦 哈班德

这个 数量 模块最初由 TJ 霍洛韦楚克。 。 。 .

使用量

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]);

数量 允许您在查询字符串中创建嵌入对象, 绕出有方括号的子密钥的名称 []。 。 。 . 例如,字符串 'foo[bar]=baz' 转换为:

assert.deepEqual(qs.parse('foo[bar]=baz'), {
    foo: {
        bar: 'baz'
    }
});

使用时 plainObjects 选项,通过 Object.create(null) 因此,你应该知道,原型方法将不存在,用户可以将这些名称设定在他们喜欢的任何价值上:

var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });
assert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } });

默认参数将覆盖对象原型上的属性会被忽略, 如果您希望保留这些字段的数据, 请使用 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'
        }
    }
});

默认情况下, 当嵌入对象 数量 只有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);

这个深度可以通过通过 depth 选项到 qs.parse(string, [options])数字 :

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' } } });

深度限制有助于减轻滥用情况 数量 用于解析用户输入,建议保持一个合理的小数字.

出于类似原因,默认 数量 仅解析可达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' });

也可以通过一个可选的分隔符:

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' } });

如果你必须处理 遗留浏览器或服务,有 也支持以等价- 8859-1 来解码 %- 编码的 octets :

var oldCharset = qs.parse('a=%A7', { charset: 'iso-8859-1' });
assert.deepEqual(oldCharset, { a: '§' });

一些服务增加了初始 utf8=✓ 值到窗体这样旧 Internet Explorer 版本更有可能提交表格为 utf- 8. 此外, 服务器可以检查值是否为错误 检查标记字符的编码和检测查询字符串 或者说 application/x-www-form-urlencoded 躯体是 没有 以 utf-8,例如,如果表格有: accept-charset 参数或 包含页面的字符集不同.

数量 通过 charsetSentinel 选项. 如果指定,则 utf8 参数将从 返回对象。 将用来切换到 iso-8859-1编号 :utf-8 模式,取决于检查标记是如何编码的.

重要: 当您指定两个 charset 选项和 charsetSentinel 选项 charset 当它被覆盖的时候 请求中包含 utf8 实际参数 字符集可以推断. 在这个意义上 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 模式.

分析阵列

数量 也可以使用相近的 [] 标记 :

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'] });

注意一个数组中的索引和对象中的密钥的唯一区别是括号之间的值必须是数字 以创建数组。 当创建带有特定索引的数组时, 数量 将会将一个稀疏的数组压缩到仅保存现有值 他们的顺序:

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'] });

数量 还可以将数组中指定索引限制为最大索引 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' } });

要完全禁用数组解析, 设置 parseArrays 改为 false。 。 。 .

var noParsingArrays = qs.parse('a[]=b', { parseArrays: false });
assert.deepEqual(noParsingArrays, { a: { '0': 'b' } });

如果你混合标记, 数量 将这两个项目合并为对象:

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' }] });

有些人用逗号加入数组, 数量 能够解析 :

var arraysOfObjects = qs.parse('a=b,c', { comma: true })
assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })

(单位:千美元)此选项无法转换嵌入对象, 例如 a={b:1},{c:d}(中文(简体) )

字符串

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数字 :

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
}})

(注: encoder 如果 encode 实值 false(中文(简体) )

模拟 encoder 有一个 decoder 选项 parse 以覆盖属性和值的解码:

var decoded = qs.parse('x=z', { decoder: function (str) {
    // Passed in values `x`, `z`
    return // Return decoded string
}})

通过使用向编码器提供的类型参数,可以使用不同的逻辑编码密钥和值:

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
    }
}})

类型参数也提供给解码器:

var decoded = qs.parse('x=z', { decoder: function (str, defaultEncoder, charset, type) {
    if (type === 'key') {
        return // Decoded key
    } else if (type === 'value') {
        return // Decoded value
    }
}})

超过这一点的例子将显示为输出没有为清晰度编码URI. 请注意,在这种情况下的返回价值 将 在实际使用时被URI编码.

当数组被字符串化时,默认情况下它们会被赋予明确的指数:

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'] }, { arrayFormat: 'comma' })
// 'a=b,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'

空字符串和空值会省略该值,但等值符号(=)仍然保留:

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=');

查询字符串可选择预留一个问号:

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');

如果您仅想要推翻序列化 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'

处理 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');

如果你与遗留系统沟通 你可以切换到 iso-8859-1 使用 charset 选项 :

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和其他人在提交表格时的做法类似.

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, (中文(简体) ), 财务报告和审定财务报表 iso-8859-1 支助也通过 charset 参数.

如果您想要将查询字符串编码到不同的字符集(即:. 移动 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和RFC 1738空间编码

RFC3986 用作默认选项并编码 ' to % 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://tidelift.com/security (中文(中国大陆) ) 如果你有 潜在的安全弱点报告.

qs 企业

作为Tidelift订阅的一部分提供

Qs和其他上千个软件包的维护者与Tidelift合作,为您用于构建应用程序的开源依赖提供商业支持和维护. 节省时间,降低风险,改善代码健康,同时向维护者支付使用的确切依赖性. 学多取.

版本与发布记录

@d-fischer/qs 共发布 3 个版本,首次发布于 2020/04/03;过去 12 个月发布了 0 个版本。

版本发布日期距今
7.0.2latest2020/04/036 年前
7.0.12020/04/036 年前
7.0.02020/04/036 年前

关于 @d-fischer/qs 的常见问题

@d-fischer/qs 是什么?

一个支持嵌入和阵列的查询字符串解析器,其深度限制

如何安装 @d-fischer/qs?

在项目目录运行 npm install @d-fischer/qs,或使用 pnpm add / yarn add / bun add。

@d-fischer/qs 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@d-fischer/qs 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@d-fischer/qs 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=0.6。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@d-fischer/qs 可以商用吗?

@d-fischer/qs 使用 BSD-3-Clause 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。另外不得用原作者名义为衍生产品背书。以上为概括,不构成法律意见,请以许可原文为准。

@d-fischer/qs 的下载量有多少?

根据 npm 官方下载统计,@d-fischer/qs 在 2026/10/11 – 2026/11/09 这 30 天内被下载 22,006 次,日均约 734 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@d-fischer/qs 还在维护吗?

最新版本 7.0.2 发布于 2020/04/03(6 年前);至今共发布 3 个版本,其中过去 12 个月发布了 0 个;首次发布于 2020/04/03。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。