在後端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輸出結果