一个数据埋点的问题

背景:

上周负责收集收据的同事说前端埋点数据格式有问题,可是发送数据这块没有改动。怎么会出错?

发送数据代码:

let data = {
        act_k:'123',
        act_v:'456'
    }

$.ajax({
    url:'http://xxx.com',
    type:'get',
    data:data
})

需要传递给后端的格式如下:

http://xxx.com/?act_k=123&act_v=456

这次发送的数据格式:

let data = {
        act_k:'123123',
        act_v:{
            name:'name',
            age:20
        }
    }

发送如下:

http://xxx.com/?act_k=123123&act_v[name]=name&act_v[age]=20

看着数据是发送过去了,可是不符合后端接收要求:

http://xxx.com/?{"act_k":"123123","act_v":{"name":"name","age":20}}

对比两次发送的数据,看到act_k的值类型不同,第一个为字符串,第二个为对象。是不是这里的问题呢?可是前端业务代码都一样,所以只能会是 jQuery 的 ajax 方法在处理不同 data 时逻辑不同。

于是写了一个demo页面做测试,一步步跟踪发现如下代码:

// Convert data if not already a string
if ( s.data && s.processData && typeof s.data !== "string" ) {
    s.data = jQuery.param( s.data, s.traditional );
}

意思是若 data 是一个非字符串,则进行 jQuery.param 处理。

// <=1.3.2:
$.param({ a: [ 2, 3, 4 ] }); // "a=2&a=3&a=4"
// >=1.4:
$.param({ a: [ 2, 3, 4 ] }); // "a[]=2&a[]=3&a[]=4"

// <=1.3.2:
$.param({ a: { b: 1, c: 2 }, d: [ 3, 4, { e: 5 } ] });
// "a=[object+Object]&d=3&d=4&d=[object+Object]"

// >=1.4:
$.param({ a: { b: 1, c: 2 }, d: [ 3, 4, { e: 5 } ] });
// "a[b]=1&a[c]=2&d[]=3&d[]=4&d[2][e]=5"

传入我的数据:

act_k=123123&act_v[name]=name&act_v[age]=20

看来就是它了,jQuery.param 把我的数据给序列化了。如果不熟悉此方法,.serialize() 肯定在模拟表单提交时使用过。

jQuery.fn.extend( {
    serialize: function() {
        return jQuery.param( this.serializeArray() );
    },
    serializeArray: function() {
        return this.map( function() {

            // Can add propHook for "elements" to filter or add form elements
            var elements = jQuery.prop( this, "elements" );
            return elements ? jQuery.makeArray( elements ) : this;
        } )
        .filter( function() {
            var type = this.type;

            // Use .is( ":disabled" ) so that fieldset[disabled] works
            return this.name && !jQuery( this ).is( ":disabled" ) &&
                rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
                ( this.checked || !rcheckableType.test( type ) );
        } )
        .map( function( i, elem ) {
            var val = jQuery( this ).val();

            if ( val == null ) {
                return null;
            }

            if ( jQuery.isArray( val ) ) {
                return jQuery.map( val, function( val ) {
                    return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                } );
            }

            return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
        } ).get();
    }
} );

serialize 也是调用了 jQuery.param,如何解决此问题?

看逻辑判断:

if ( s.data && s.processData && typeof s.data !== "string" ) {
    s.data = jQuery.param( s.data, s.traditional );
}

processData 为 false,或者 data 为字符串就行。

方法一:

$.ajax({
    url:'http://xxx.com',
    type:'get',
    data:data,
    processData:false // 新增
})

发送数据:

http://xxx.com/?[object Object]

真够彻底,一点也不处理。

方法二:

let data = {
        act_k:'123123',
        act_v:{
            name:'name',
            age:20
        }
    }

data = JSON.stringify(data);

发送数据:

http://xxx.com/?{"act_k":"123123","act_v":{"name":"name","age":20}}

ok,符合要求。

总结:

在业务代码一直没有改动的情况下,多数是因为输入参数变动,导致框架代码逻辑分之变动。
分享到: