在后端API返回的响应头中,包含了12条set-cookie
。
百度小程序(以及微信小程序)对此的处理是:将多个同名的响应头用半角逗号,
JOIN成一长串字符串。原因是因为response.header是一个Object对象。
由于cookie信息中往往会存在Expires
过期时间信息,而这种GMT时间格式是有可能存在半角逗号的,会与header的JOIN字符冲突,所以如果简单使用
cookieStr.split(',')
复制代码
来切割cookie,会导致cookie解析不正确。看一下cookie样本:
let badCookie = `
route=xxx; Path=/,
authId=xxx;domain=.xxx.com;path=/;HTTPOnly;,
secureToken=xxx;domain=.xxx.com;path=/;secure;HTTPOnly;,
path=/; domain=.xxx.com; Max-Age=1728000;
Expires=Sat, 22-Dec-2018 05:30:39 GMT,
`;
复制代码
采用的处理方式也比较简单粗暴:进行特殊值保护。
在切割cookie之前,先对引起冲突的日期做转换,保护日期值不会被split破坏。
export const pluginFixGMTDateTime = cookie =>
(cookie + '').replace(
/\=(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*\,/g,
'=$1'
);
复制代码
除此之外,我们还需要对cookie做头尾去空字符串、整体去换行符等清理工作。
export const pluginTrim = s =>
(s + '').replace(/^\s+|\s+$/g, '');
export const pluginLinear = s =>
(s + '').replace(/[\r\n]+/g, '');
复制代码
用插件形式写清理方法,目的是方便以后扩展。
在进行切割之前,我们先把插件组织起来,做一个专门的清理工具:
export const prepareString = (s, ...plugin) => {
plugin = [
pluginTrim,
pluginLinear,
pluginFixGMTDateTime,
...plugin
];
return plugin.length < 1
? s
: plugin.length === 1
? plugin[0](s)
: plugin.reduce((a, b) =>
typeof(a) === 'function' ? b(a(s)) : b(a)
);
};
复制代码
在cookie切割之后,我们还需要将每一个key-value结构的键值对取出来,依然用正则:
export const cookieItemRegExp =
/([\w\-_]+)(\s*\=\s*((Mon|Tue|Wed|Thu|Fri|Sat|Sun).+?GMT|[^;\,]+))?/g
;
复制代码
这里的正则其实也包含特殊值保护,但cookie是一种特殊结构,需要按条解析,所以不能直接用此正则匹配cookie全文。
现在都已经准备好了,我们可以开始动手解析cookie了:
export const cookieParser = (cookie, ...plugin) => {
let cleanCookie = prepareString(cookie + '', ...plugin);
let cookieSegments = cleanCookie.split(',').map(s => pluginTrim(s));
let cookies = [];
cookieSegments.forEach(cookieStr => {
if(cookieStr.length < 1) return;
let ms = cookieStr.match(cookieItemRegExp), cookieObj = {};
if(ms){
ms.forEach(m => {
let idx = m.indexOf('='), key, val;
if(idx > -1){
key = m.substr(0, idx);
val = m.substr(idx + 1);
} else {
key = m;
val = null;
}
cookieObj[key] = val;
})
}
cookies.push({ cookieStr, cookieObj })
});
return cookies;
};
复制代码
解析badCookie输出结果