npm qs模块使用详解


Posted in Javascript onFebruary 07, 2020

本文基本使用谷歌翻译加上自己的理解,权当加深记忆。

npm

简介

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
ASP 过滤数组重复数据函数(加强版)
May 31 Javascript
jquery URL参数判断,确定菜单样式
May 31 Javascript
关于 文本框默认值 的操作js代码
Jan 12 Javascript
JavaScript 和 Java 的区别浅析
Jul 31 Javascript
js获取系统的根路径实现介绍
Sep 08 Javascript
基于BootStrap环境写jQuery tabs插件
Jul 12 Javascript
jQuery实现用户信息表格的添加和删除功能
Sep 12 jQuery
vue使用监听实现全选反选功能
Jul 06 Javascript
node使用Mongoose类库实现简单的增删改查
Nov 08 Javascript
axios+Vue实现上传文件显示进度功能
Apr 14 Javascript
详解vue 2.6 中 slot 的新用法
Jul 09 Javascript
解决layui laydate 时间控件一闪而过的问题
Sep 28 Javascript
Js参数RSA加密传输之jsencrypt.js的使用
Feb 07 #Javascript
Vue使用JSEncrypt实现rsa加密及挂载方法
Feb 07 #Javascript
jQuery实现获取多选框的值示例
Feb 07 #jQuery
微信小程序canvas开发水果老虎机的思路详解
Feb 07 #Javascript
Node.js 在本地生成日志文件的方法
Feb 07 #Javascript
node.js 微信开发之定时获取access_token
Feb 07 #Javascript
jQuery操作选中select下拉框的值代码实例
Feb 07 #jQuery
You might like
将兴奋、喜悦和坎加斯带到戴安娜:亚马逊公主
2020/03/03 欧美动漫
phpexcel导入excel数据使用方法实例
2013/12/24 PHP
php的zip解压缩类pclzip使用示例
2014/03/14 PHP
浅谈使用 PHP 进行手机 APP 开发(API 接口开发)
2014/08/11 PHP
javascript支持firefox,ie7页面布局拖拽效果代码
2007/12/20 Javascript
在jQuery中 常用的选择器介绍
2013/04/16 Javascript
jQuery的text()方法用法分析
2014/12/20 Javascript
jQuery实现新消息闪烁标题提示的方法
2015/03/11 Javascript
浅谈javascript中onbeforeunload与onunload事件
2015/12/10 Javascript
jQuery自定义数值抽奖活动代码
2016/06/11 Javascript
深入讲解xhr(XMLHttpRequest)/jsonp请求之abort
2017/07/26 Javascript
JS实现二维数组横纵列转置的方法
2018/04/17 Javascript
jQuery实现遍历XML节点和属性的方法示例
2018/04/29 jQuery
js 将线性数据转为树形的示例代码
2019/05/28 Javascript
Vue.js实现大转盘抽奖总结及实现思路
2019/10/09 Javascript
js+cavans实现图片滑块验证
2020/09/29 Javascript
[36:17]DOTA2上海特级锦标赛 - VGL音乐会全集
2016/03/06 DOTA
python中pygame模块用法实例
2014/10/09 Python
python实现多层感知器
2019/01/18 Python
详解python配置虚拟环境
2019/04/08 Python
pyqt5 QScrollArea设置在自定义侧(任何位置)
2019/09/25 Python
python GUI库图形界面开发之PyQt5状态栏控件QStatusBar详细使用方法实例
2020/02/28 Python
Python实现一个简单的毕业生信息管理系统的示例代码
2020/06/08 Python
Python+OpenCV图像处理——图像二值化的实现
2020/10/24 Python
FOREO官方网站:LUNA露娜洁面仪
2016/11/28 全球购物
乔丹诺(Giordano)酒庄德国官网:找到最好的意大利葡萄酒
2017/12/28 全球购物
SmartBuyGlasses英国:购买太阳镜和眼镜
2018/01/29 全球购物
美国折扣香水网站:The Perfume Spot
2020/12/12 全球购物
简述synchronized和java.util.concurrent.locks.Lock的异同
2014/12/08 面试题
EJB timer的种类
2014/10/28 面试题
护理毕业生自我鉴定
2014/02/11 职场文书
个人安全承诺书
2014/05/22 职场文书
远程培训的心得体会
2014/09/01 职场文书
班主任工作实习计划
2015/01/16 职场文书
关于运动会的宣传稿
2015/07/23 职场文书
Redis中key的过期删除策略和内存淘汰机制
2022/04/12 Redis