nuxt.js请求参数是数组的问题

背景:前两天线上出现了训练营详情无法跳转的问题,定位之后是由于前端在调接口时传参有问题,导致的接口返回参数不对导致的。

原因分析:

       问题接口:http://o2o.dailyyoga.com.cn/620/yogao2school/session/马赛克?session_id=759&dy=1&app[]=1&app[]=1&version[]=7.14.0.0&version[]=7.14.0.0&sid[]=fbdaf97d954665a879a5ce56b012f7c9&sid[]=fbdaf97d954665a879a5ce56b012f7c9&uid[]=117261794&uid[]=117261794&visitor_uid[]=117261794&visitor_uid[]=117261794&time[]=1587375765&time[]=1587375765&timezone[]=8&timezone[]=8&channels[]=200001&channels[]=200001&type[]=2&type[]=2&deviceId[]=eff881a9ef0239c36b855fb5bce1023fbe57d561&deviceId[]=eff881a9ef0239c36b855fb5bce1023fbe57d561&sign[]=08456ae9ab2a23a759f7ecab86431843&sign[]=9840a13d9798daca44d91ad60eee938e

       可以看到接口里面拼接的uid、sid等参数后面多了一个[],这个问题是怎么出现的呢?

       首先,h5这边请求接口时的逻辑如下:

       

       通过请求库axios来发起请求,其中query为该次请求需要拼接到接口后面的参数。

       query的值是由客户端拼接到h5链接后面的参数解析之后得到,由于7.15版本之前ios在训练营详情页面对应的参数拼接了两遍的原因(抓到的链接:https://node.dailyyoga.com.cn/o2_detail/?session_id=760&dy=1&app=1&app=1&version=7.13.1.0&version=7.13.1.0&sid=b4f6ddcd51a206c189386cf6643ad686&sid=b4f6ddcd51a206c189386cf6643ad686&uid=89560032&uid=89560032&visitor_uid=89560032&visitor_uid=89560032&time=1587378408&time=1587378408&timezone=8&timezone=8&channels=200001&channels=200001&type=2&type=2&deviceId=eff881a9ef0239c36b855fb5bce1023fbe57d561&deviceId=eff881a9ef0239c36b855fb5bce1023fbe57d561&sign=e3c4c2f9bdfd15715485365bc8e9387f&sign=0faa69df1cbfa94d1a861942ba2ae7b0),导致这边query解析出来里面有些key值对应成了数组,就像这样:

{
    session_id: '738',
    dy: '1',
    app: [1, 1],
    uid: ['89560032', '89560032'],
    sid: ['b4f6ddcd51a206c189386cf6643ad686', 'b4f6ddcd51a206c189386cf6643ad686'],
    version: ['7.13.1.0', '7.13.1.0']
}

       这个时候请求库在往url上拼接对应的参数的时候出现了问题,下面是请求库把参数拼接到url后面时的源码,这段代码生成了上面说的有问题的url请求链接:

/**
 * Build a URL by appending params to the end
 *
 * @param {string} url The base of the url (e.g., http://www.google.com)
 * @param {object} [params] The params to be appended
 * @returns {string} The formatted url
 */
module.exports = function buildURL(url, params, paramsSerializer) {
  /*eslint no-param-reassign:0*/
  if (!params) {
    return url;
  }

  var serializedParams;
  if (paramsSerializer) {
    serializedParams = paramsSerializer(params);
  } else if (utils.isURLSearchParams(params)) {
    serializedParams = params.toString();
  } else {
    var parts = [];

    utils.forEach(params, function serialize(val, key) {
      if (val === null || typeof val === 'undefined') {
        return;
      }

      if (utils.isArray(val)) {
        key = key + '[]'; // 这里是重点,参数里面如果有数组,对应的key后面就会加上[]
      } else {
        val = [val];
      }

      utils.forEach(val, function parseValue(v) {
        if (utils.isDate(v)) {
          v = v.toISOString();
        } else if (utils.isObject(v)) {
          v = JSON.stringify(v);
        }
        parts.push(encode(key) + '=' + encode(v));
      });
    });

    serializedParams = parts.join('&');
  }

  if (serializedParams) {
    var hashmarkIndex = url.indexOf('#');
    if (hashmarkIndex !== -1) {
      url = url.slice(0, hashmarkIndex);
    }

    url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
  }

  return url;
};

       然后,再来看看为啥这个query会被解析成这样子的。

       训练营详情页面通过服务端渲染(vue框架集成的nuxt.js)重构,nuxt中提供了一个方法,用来在页面加载出来前异步获取数据。

async asyncData({ params, query, error, $axios, req, res })

       其中query参数是nuxt自己提供的,跟vue获取query的方法一样(这是vue处理query的源码地址:https://github.com/vuejs/vue-router/blob/dev/src/util/query.js),下面是其中一段核心代码:

function parseQuery (query: string): Dictionary<string> {
  const res = {}

  query = query.trim().replace(/^(\?|#|&)/, '')

  if (!query) {
    return res
  }

  query.split('&').forEach(param => {
    const parts = param.replace(/\+/g, ' ').split('=')
    const key = decode(parts.shift())
    const val = parts.length > 0 ? decode(parts.join('=')) : null

    if (res[key] === undefined) {
      res[key] = val
    } else if (Array.isArray(res[key])) {
      res[key].push(val) // 如果有多于两个以上相同的key的参数,会继续往数组里面push
    } else {
      res[key] = [res[key], val] // 如果对象里面已经有了这个key值,是这个时候还有一个
                                 // 相同的key值,此时该key对应的value就会变成数组形式
    }
  })

  return res
}

       训练营项目重构前,解析链接里面的参数是我们自己写的方法,对链接中有相同的参数会做去重处理,重复的key会取链接中最后一个的值。

    DY.getUrlQueryObj = function (url) {
        url = url == null ? window.location.href : url;
        var search = url.substring(url.lastIndexOf("?") + 1);
        var obj = {};
        var reg = /([^?&=]+)=([^?&=]*)/g;
        search.replace(reg, function (rs, $1, $2) {
            var name = decodeURIComponent($1);
            var val = decodeURIComponent($2);
            obj[name] = val;
            return rs;
        });
        return obj;
    };

解决办法:

       重构的项目中,在使用集成好的query之前,先做一遍去重,再使用。

        let queryNew = {};
        let keyLength = Object.keys(query);
        for(let i = 0; i < keyLength.length; i++) {
            // 处理链接里面参数重复拼接  去重
            let isArray = query[keyLength[i]] instanceof Array;
            if(isArray && query[keyLength[i]].length > 1) {
                queryNew[keyLength[i]] = query[keyLength[i]][0];
            } else {
                queryNew[keyLength[i]] = query[keyLength[i]];
            }
        }

       总结:框架在给我们的开发带来便利的同时,也埋下了一些我们无法预估的坑,后面的开发过程中还是需要多去关注一些框架底层的实现机制,多看源码,不断学习,不断提升。

猜你喜欢

转载自blog.csdn.net/longgege001/article/details/117022144