请求IPFS数据触发FetchError,Express应用崩溃求助
IPFS请求返回非JSON导致Express崩溃的解决方法
错误日志
FetchError: invalid json response body at https://gateway.pinata.cloud/ipfs/Qmc79GYqhAvU1qpDCJ4Ji37fWt2EoerqqxHb7e7bxakkFX reason: Unexpected token < in JSON at position 0 at E:\MEDIBLOCK\backend\node_modules\node-fetch\lib\index.js:273:32 at processTicksAndRejections (node:internal/process/task_queues:96:5) at async Object.getDataFromIPFS (file:///E:/MEDIBLOCK/backend/controller/auth/ipfsServices.js:57:20) at async getAllDoctors (file:///E:/MEDIBLOCK/backend/controller/auth/doctorController.js:147:24) { type: 'invalid-json' }
错误原因
这个错误核心是:请求的IPFS网关返回的不是JSON数据,而是HTML(开头的<是HTML标签起始符号),大概率是以下情况:
- IPFS哈希无效或对应文件未上传成功
- Pinata网关返回了错误页面(如404、限流提示)
- 合约中存储的IPFS哈希存在拼写/格式错误
解决步骤
1. 验证IPFS哈希有效性
直接在浏览器打开报错的IPFS链接https://gateway.pinata.cloud/ipfs/Qmc79GYqhAvU1qpDCJ4Ji37fWt2EoerqqxHb7e7bxakkFX:
- 如果返回HTML错误页(比如404),说明哈希无效,需确认合约中存储的哈希是否正确,以及IPFS文件是否上传成功
- 如果返回正常JSON,再排查代码请求逻辑
2. 强化IPFS请求的错误处理
当前getDataFromIPFS的错误处理不完整:仅捕获请求异常,但如果返回200状态码但内容不是JSON,response.json()会抛错,且当前catch仅打印日志,返回undefined会导致后续result.phoneNo赋值崩溃。
修改后的getDataFromIPFS代码:
async getDataFromIPFS(hash) { try { const response = await fetch(`https://gateway.pinata.cloud/ipfs/${hash}`, { mode: "cors" }); // 先检查响应状态是否正常 if (!response.ok) { throw new Error(`IPFS请求失败,状态码:${response.status}`); } // 单独捕获JSON解析错误 try { const data = await response.json(); return data; } catch (parseError) { console.error(`解析IPFS数据失败,哈希:${hash}`, parseError); // 返回标记无效的数据,避免后续崩溃 return { invalid: true, hash }; } } catch (error) { console.error(`请求IPFS失败,哈希:${hash}`, error); return { failed: true, hash }; } },
3. 在医生数据获取逻辑中过滤无效数据
修改getAllDoctors,跳过无效的IPFS返回结果,避免因undefined或错误对象导致崩溃:
async getAllDoctors(req, res, next) { try { const contract = new web3.eth.Contract(hospitABI, contarct_address); const allAddress = await contract.methods.getDoctorAddresses().call(); let allInfo = []; for (let i = 0; i < allAddress.length; i++) { const doctor = await contract.methods .GetDoctorByAddress(allAddress[i]) .call(); allInfo = allInfo.concat(doctor); } const info = []; for (let i = 1; i < allInfo.length; i++) { const hash = allInfo[i]["0"]; const result = await ipfsServiceController.getDataFromIPFS(hash); // 仅处理有效数据 if (result && !result.invalid && !result.failed) { result.phoneNo = allInfo[i]["1"]; info.push(result); } else { console.warn(`跳过无效医生数据,哈希:${hash}`); } } res.send(info); } catch (error) { console.log("main -> " + error); // 给前端返回错误响应,避免请求挂起 res.status(500).send({ error: "获取医生列表失败" }); } }
4. 尝试更换IPFS网关
Pinata网关可能存在访问限制,可替换为其他公共网关测试,比如:
https://ipfs.io/ipfs/${hash}https://cloudflare-ipfs.com/ipfs/${hash}
内容的提问来源于stack exchange,提问作者Aditya Waskar
相关产品推荐
相关产品推荐

