苦逼前端

querystring的坑

Nodejs2015-10-13 18:50

记得上次博客改版的时候遇到过一个问题,感觉数据从前端post到服务端后发生了不正常的改变,当时也没有多想,写了几行hack解决了。幸亏当时留下了大量的注释,否则今天再看见那段hack肯定摸不着头脑:

req.body = querystring.parse(postData.join(''));
postData = null;
/*  此处有坑:
    1.若前端传过来的value是个数组,则key会加'[]'后缀
    如,前端post过来的data是{arr: [1,2,3]},则实际接收时,key是'arr[]'
    2.若传过来的数组中只有一个元素,实际接收到的value是这个元素
    如,前端post过来的data是{arr: [1]},则实际接收时,value是1
 */
for(var i in req.body){
    //命中1
    if(/^.+\[\]$/.test(i)){
        //命中2
        if(req.body[i].constructor != Array){
            req.body[i] = [req.body[i]];
        }
        req.body[i.replace('[]', '')] = req.body[i];
        delete req.body[i];
    }
}

现在再看看这段代码,绝对是《基于巧合的编程》的典范啊,完全不知道json的序列化,如果接收的数据碰上是多个对象组成的数组或者多维数组,绝对挂的体无完肤啊,好在前端也是自己写,没有传递更复杂的数据,"恰好"没有发生问题。

但即使是传递的数据被序列化了,好像也不符合大多数前端库封装的ajax中对json序列化的规矩啊,如jQuery和Zepto,他们分别都是使用了$.param这个方法对传递的数据做处理:

//示例代码
var $ = require('jQuery');//或者Zepto
//传递一维对象
var simple = {a: 1, b: 2};
$.param(simple);
//得到
//a=1&b=2

//传递二维或多维
var complex = {
    a: [1, 2, 3],
    b: [
        {
            c: 4,
            d: 5,
            e: 6
        },
        {
            f: 7,
            g: 8,
            h: 9
        }
    ],
    c: {
        d: 10
    }
};
$.param(complex);
//将结果decodeURIComponent之后得到:
//a[]=1&a[]=2&a[]=3&b[0][c]=4&b[0][d]=5&b[0][e]=6&b[1][f]=7&b[1][g]=8&b[1][h]=9&c[d]=10

上面的结果大致可以看出json序列化的规律,其实我对其他语言都能约定俗成的使用同一规则表示惊奇。但回过头看看我大node:

var querystring = require('querystring');
//传递一维对象序列化后的字符串
var simple = 'a=1&b=2';
querystring.parse(simple);
//得到
//{a: '1', b: '2'}

//传递二维或多维对象序列化后的字符串
var complex = `a[]=1&a[]=2&a[]=3&b[0][c]=4&b[0][d]=5&b[0][e]=6&b[1][f]=7&b[1][g]=8&b[1][h]=9&c[d]=10`;

querystring.parse(complex);
// 得到:
// { 
//     'a[]': ['1', '2', '3'],
//     'b[0][c]': '4',
//     'b[0][d]': '5',
//     'b[0][e]': '6',
//     'b[1][f]': '7',
//     'b[1][g]': '8',
//     'b[1][h]': '9',
//     'c[d]': '10'
// }

//并且
querystring.parse('a[]=1');
//得到
//{
//    'a[]': 1
//}
//还有url对象的url.parse(urlStr, true),
//官网明确说明解析query也使用了querystring.parse

虽然说querystring可能不是标准的用于反序列化模块,但url模块的parse函数,官网给出的文档上写着如果第二个参数传入true的话,最终url.parse得出来对象的query属性,将是一个解析了search的对象,并且是用querystring来解析的:

var url = require('url');
url.parse('http://localhost:3000/user?a[]=1&a[]=2&b[]=3#4', true);
// 将得到:
// {
//     protocol: 'http:',
//     slashes: true,
//     auth: null,
//     host: 'localhost:3000',
//     port: '3000',
//     hostname: 'localhost',
//     hash: '#4',
//     search: '?a[]=1&a[]=2&b[]=3',
//     query: { 'a[]': [ '1', '2' ], 'b[]': '3' },
//     pathname: '/user',
//     path: '/user?a[]=1&a[]=2&b[]=3',
//     href: 'http://localhost:3000/user?a[]=1&a[]=2&b[]=3#4'
// }

也就是说,只有当前端传过来的数据是一维对象序列化的结果,才能被正确的解出来。所以才有了前面的笑话... 目前还没有发现node的哪个自带模块可以正常的反序列化,第三方的倒是有不少,推荐qs

测试node版本:v0.10.35、v4.0.0、v4.1.1

评论(0)
  • 暂无评论,求挽尊...
还可输入200个字