JS Fetch返回result为undefined问题排查求助
问题排查:部分iOS Safari用户调用Fetch API时出现"result = undefined"错误
问题概述
代码对绝大多数用户正常运行,但部分使用iOS 16.6系统的Mobile Safari用户遇到错误,日志显示Error: result = undefined。
后端Express.js代码
router.post("/do-something", async (req, res) => { res.status(200).send({ token: '8lAp5hVLUXZFq/laslTWd+GOEcEOeJ58X+Eu2Elg0Q', expirationDate: 1695680882748, email: 'manu.macron@gmail.com' }); }) router.post("/error-log", async (req, res) => { const { error } = req.body fs.appendFileSync('logs/something-went-wrong.txt', `${ new Date().toLocaleString() } - ${ error }\n`); fs.appendFileSync('logs/something-went-wrong.txt', JSON.stringify(UAParser(req.headers['user-agent']), null, 4) + "\n"); return res.sendStatus(204) })
前端Fetch代码
fetch("api.com/do-something", { method: "POST", headers: { 'Content-Type': 'application/json', } }).then(async result => { const _json = result?.json ? await result.json() : {}; if(!result?.ok) { const error = new Error(result?.statusText || "result = undefined") error.code = result?.status || 418 error.details = Object.keys(_json).length ? _json : null throw error; } // Do something with the token }).catch(error => { fetch("api.com/error-log", { method: "POST", headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ error: `Someone did fetch but ${error?.details ? JSON.stringify(error.details) : (error instanceof Error ? error.toString() : error)}` }) }).then(_ => true /*do nothing*/ ) })
错误日志
# logs/something-went-wrong.txt 29/08/2023 13:57:10 - Someone did fetch but Error: result = undefined { "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1", "browser": { "name": "Mobile Safari", "version": "16.6", "major": "16" }, "engine": { "name": "WebKit", "version": "605.1.15" }, "os": { "name": "iOS", "version": "16.6" }, "device": { "vendor": "Apple", "model": "iPhone", "type": "mobile" }, "cpu": {} }
排查分析与调试线索
可能的原因
- 浏览器隐私拦截:iOS 16.6的Mobile Safari开启智能跟踪防护(ITP)时,可能会拦截跨域请求或修改响应,导致
Response对象的statusText属性缺失,触发错误信息。 - URL路径问题:前端Fetch使用相对路径
api.com/do-something,缺少协议头(http/https),当页面环境变化时(比如PWA离线模式、不同协议的页面),请求可能异常,导致响应对象不完整。 - 网络波动/响应截断:网络不稳定时,响应数据不完整,浏览器无法正确解析
Response对象,导致statusText为undefined,result.ok为false。 - 前端错误处理逻辑缺陷:
result?.json ? await result.json() : {}的判断逻辑有风险,若响应非JSON格式,await result.json()会抛出错误,但当前错误日志未体现这种情况,说明不是主要原因。
调试与修复建议
- 增强前端错误日志:在错误信息中加入响应的状态码、请求URL、响应头、JSON解析错误等细节,便于定位问题。修改前端代码如下:
.then(async result => { let _json = {}; const responseDetails = { status: result?.status, statusText: result?.statusText, url: result?.url, headers: result?.headers ? Object.fromEntries(result.headers.entries()) : {} }; try { _json = await result.json(); } catch (jsonErr) { responseDetails.jsonParseError = jsonErr.toString(); } if (!result?.ok) { const error = new Error(result?.statusText || "result = undefined"); error.code = result?.status || 418; error.details = { ...responseDetails, responseBody: _json }; throw error; } // 处理token逻辑 }) - 使用完整URL:将Fetch的目标URL改为带协议的完整路径(如
https://api.com/do-something),避免相对路径引发的环境适配问题。 - 检查后端访问日志:查看后端是否收到这些异常请求,记录请求的IP、头信息、返回状态码,确认请求是否到达后端,或是否被中间层(CDN、代理)修改。
- 复现测试:使用iOS 16.6的Mobile Safari开启ITP功能,模拟用户环境复现问题,验证是否为隐私拦截导致。
- 优化后端日志:在
/do-something路由中添加请求日志,记录用户UA、请求时间、IP等信息,对比正常请求与异常请求的差异。
内容的提问来源于stack exchange,提问作者Simon Trichereau
相关产品推荐
相关产品推荐

