Firefox中fetch API二次请求未等首次响应致custombackendtoken缺失
问题现象
调用fetchData方法时,Firefox中部分二次请求未携带custombackendtoken请求头,Chrome无此问题。服务器日志显示请求顺序为:request1(获取Token的请求)输入、request2(业务请求)输入、request2输出、request1输出,确认二次请求确实未携带目标头,多次刷新页面可复现该异常。
排查思路
排查异步竞态问题
从日志顺序看,request2在request1的响应返回前就已发起,说明getAuthToken内的异步操作未正确阻塞二次请求。需确认retrieveStoredToken或createToken是否存在未正确await的异步逻辑,或是Firefox下缓存/存储逻辑出现竞态,导致bearerToken未就绪就执行了二次请求。验证Headers对象兼容性
Firefox对RequestInit.headers的处理可能与Chrome存在差异,直接解构对象赋值可能引发异常。尝试改用HeadersAPI构建请求头:const reqOptions: RequestInit = options || {}; reqOptions.headers = new Headers(reqOptions.headers); reqOptions.headers.set('custombackendtoken', bearerToken);调整Firefox缓存策略
retrieveStoredToken通过请求${proxyPath}/token获取缓存Token,Firefox的缓存优先级可能更高,导致retrieveStoredToken返回undefined的时机与Chrome不一致,触发createToken异步流程时二次请求已发起。可添加缓存禁用参数:const response = await fetch(`${proxyPath}/token`, { credentials: 'same-origin', cache: 'no-store' });添加调试日志定位时机
在getAuthToken的关键节点(进入函数、retrieveStoredToken返回后、createToken返回后、最终return前)添加日志,同时在fetchData中记录bearerToken值和二次请求发起时间,对比Firefox与Chrome的日志差异,确认Token是否在二次请求发起前已正确获取。检查Firefox隐私安全设置
Firefox的隐私模式、跟踪保护或第三方Cookie设置可能影响retrieveStoredToken请求的Credentials传递,导致无法获取缓存Token。测试关闭隐私模式或调整安全设置后是否仍复现问题。
相关代码
export const fetchData = async (input: RequestInfo, options?: RequestInit, proxyPath?: string): Promise<Response> => { try { // first request const bearerToken = await getAuthToken(proxyPath); const reqOptions: RequestInit = options || {}; reqOptions.headers = { ...reqOptions.headers, custombackendtoken: bearerToken, }; // second request return await fetch(input, reqOptions); } catch (e) { console.error('Error.......', e); throw new Error(`Error....... Error: ${e}`); } }; //getAuthToken export default async (proxyPath: string): Promise<string> => { let token = await retrieveStoredToken(proxyPath); if (!token) { token = await createToken(); if (!token) { throw new Error('Token can not be created!'); } storeToken(proxyPath, token); } return token; }; //retrieveStoredToken const retrieveStoredToken = async (proxyPath: string): Promise<string | undefined> => { try { const response = await fetch(`${proxyPath}/token`, { credentials: 'same-origin', }); return await response.json(); } catch (e) { console.error('Error ..........', e); } return undefined; };
内容的提问来源于stack exchange,提问作者Musti

