code-security.md 3.86 KB

代码健壮性

原则: 在接口数据返回失败或者错误的情况下,尽量保证页面正常打开,有问题的地方可以不显示内容。

数据校验

目前在我们的代码中最常见的两个异常是 - ReferenceError: a is not defined - TypeError: Cannot read property 'c' of undefined

处理原则

- 以function为单位,每个function确保自己内部的代码,数据安全访问
- 每个function不信任输入参数,在使用时都需要校验
- 2层以上的数据访问可以借助lodash的has或者get方法处理
- function 的私有变量都有默认值

例子

  • 有问题的写法

    const func = data => {
        let result = {
            name: 'name',
            size: data.opts.size,
            colors: []
        };
    
        let theData = data.data;
        let goods = theData.goods;
    
        goods.forEach(g => {
            if (g.opts && g.opts.colors) {
                let colors = g.opts.colors;
                let name = g.brandName.toLowerCase();
    
            }
        });
    };
  • 推荐写法

    const func = data => {
        let result = {
            name: 'name',
            size: _.get(data, 'opts.size'),            //使用lodash, get
            colors: []
        };
    
        let theData = data.data || {};                  //添加默认值
        let goods = theData.goods;
    
        _.forEach(goods, g => {                         //使用lodash.forEach替代Array.prototype.forEach
            if (_.has(g, 'opts.color.name')) {
                // ...
            }
    
            let name = _.toLower(g.brandName);          //一些js prototype的方法,可以使用lodash的工具方法替代
        });
    };
  1. 获取对象属性时,需判断对象是否定义

        let func = data => {
            let a = data.data.a;
            let b = data.b;
            let c = data.list[0].name;
    
            b.c;
            // ...
        };

    解决: 使用lodash get方法获取属性,赋值给局部变量时,需要给默认值

        let func = data => {
            let a = _.get(data, 'data.a');
            let b = data.b || {};
            let c = data.list[0] || {};
    
            b.c;
            c.name;
            // ...
        };
    1. 调用String.prototype、Array.prototype 的方法时,需要判断变量是否定义、或者类型是否正确
        let s = data.s;
    
        s = s.toLowerCase();   // s 可能为定义
    
        let list = data.list || [];
    
        list.forEach(console.log);  // list不一定是数组

    解决: 原生对象的一些方法,可以使用lodash的方法替代

        s = _.toLower(s); 
    
        _.forEach(data.list, console.log);

代码容错性

原则: 在接口数据返回失败或者错误的情况下,尽量保证页面正常打开,有问题的地方可以不显示内容。

Promise.all

在使用 Promise.all 并行请求多个接口时,如果其中一个失败,那么整个Promise.all都返回失败

在一些情况下,比如商品详情页请求了6-7个接口,其中有的数据没有访问,对页面并不会有太大影响,更不会影响整个购物流程,那么对失败的接口,我们给一个默认值

Since yoho-node-lib@0.0.48

        const safePromise = global.yoho.safePromise;

        let promises = [ promise1, promise2, promise3 ...];

        Promise.all(safePromise(promises)).then(result => {
            let data2 = result[1] || {};  // 当promise2接口调用失败, data2 默认为 undefined

            if (!result[2]) {             // 如果某一个接口的数据是必须的,可以自己判断抛出异常
                throw new Error('some data must get'); 
            }
        });