背景:前两天线上出现了训练营详情无法跳转的问题,定位之后是由于前端在调接口时传参有问题,导致的接口返回参数不对导致的。
原因分析:
可以看到接口里面拼接的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]];
-
}
-
}
总结:框架在给我们的开发带来便利的同时,也埋下了一些我们无法预估的坑,后面的开发过程中还是需要多去关注一些框架底层的实现机制,多看源码,不断学习,不断提升。
转载:https://blog.csdn.net/longgege001/article/details/117022144