飞道的博客

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

193人阅读  评论(0)

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

原因分析:

       问题接口: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值对应成了数组,就像这样:


  
  1. {
  2. session_id: '738',
  3. dy: '1',
  4. app: [ 1, 1],
  5. uid: [ '89560032', '89560032'],
  6. sid: [ 'b4f6ddcd51a206c189386cf6643ad686', 'b4f6ddcd51a206c189386cf6643ad686'],
  7. version: [ '7.13.1.0', '7.13.1.0']
  8. }

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


  
  1. /**
  2. * Build a URL by appending params to the end
  3. *
  4. * @param {string} url The base of the url (e.g., http://www.google.com)
  5. * @param {object} [params] The params to be appended
  6. * @returns {string} The formatted url
  7. */
  8. module.exports = function buildURL(url, params, paramsSerializer) {
  9. /*eslint no-param-reassign:0*/
  10. if (!params) {
  11. return url;
  12. }
  13. var serializedParams;
  14. if (paramsSerializer) {
  15. serializedParams = paramsSerializer(params);
  16. } else if (utils.isURLSearchParams(params)) {
  17. serializedParams = params.toString();
  18. } else {
  19. var parts = [];
  20. utils.forEach(params, function serialize(val, key) {
  21. if (val === null || typeof val === 'undefined') {
  22. return;
  23. }
  24. if (utils.isArray(val)) {
  25. key = key + '[]'; // 这里是重点,参数里面如果有数组,对应的key后面就会加上[]
  26. } else {
  27. val = [val];
  28. }
  29. utils.forEach(val, function parseValue(v) {
  30. if (utils.isDate(v)) {
  31. v = v.toISOString();
  32. } else if (utils.isObject(v)) {
  33. v = JSON.stringify(v);
  34. }
  35. parts.push(encode(key) + '=' + encode(v));
  36. });
  37. });
  38. serializedParams = parts.join( '&');
  39. }
  40. if (serializedParams) {
  41. var hashmarkIndex = url.indexOf( '#');
  42. if (hashmarkIndex !== -1) {
  43. url = url.slice( 0, hashmarkIndex);
  44. }
  45. url += (url.indexOf( '?') === -1 ? '?' : '&') + serializedParams;
  46. }
  47. return url;
  48. };

       然后,再来看看为啥这个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),下面是其中一段核心代码:


  
  1. function parseQuery (query: string): Dictionary<string> {
  2. const res = {}
  3. query = query.trim().replace( /^(\?|#|&)/, '')
  4. if (!query) {
  5. return res
  6. }
  7. query.split( '&').forEach( param => {
  8. const parts = param.replace( /\+/g, ' ').split( '=')
  9. const key = decode(parts.shift())
  10. const val = parts.length > 0 ? decode(parts.join( '=')) : null
  11. if (res[key] === undefined) {
  12. res[key] = val
  13. } else if ( Array.isArray(res[key])) {
  14. res[key].push(val) // 如果有多于两个以上相同的key的参数,会继续往数组里面push
  15. } else {
  16. res[key] = [res[key], val] // 如果对象里面已经有了这个key值,是这个时候还有一个
  17. // 相同的key值,此时该key对应的value就会变成数组形式
  18. }
  19. })
  20. return res
  21. }

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


  
  1. DY.getUrlQueryObj = function (url) {
  2. url = url == null ? window.location.href : url;
  3. var search = url.substring(url.lastIndexOf( "?") + 1);
  4. var obj = {};
  5. var reg = /([^?&=]+)=([^?&=]*)/g;
  6. search.replace(reg, function (rs, $1, $2) {
  7. var name = decodeURIComponent($ 1);
  8. var val = decodeURIComponent($ 2);
  9. obj[name] = val;
  10. return rs;
  11. });
  12. return obj;
  13. };

解决办法:

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


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

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


转载:https://blog.csdn.net/longgege001/article/details/117022144
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场