Node.js调用Marvel API时JSON.parse报Unexpected end of JSON input错误
问题现象
在Node.js环境下调用Marvel API获取数据,使用JSON.parse()解析接口返回内容时持续失败,终端抛出SyntaxError: Unexpected end of JSON input类型错误。
错误触发位置为IncomingMessage的data事件回调中执行JSON.parse的代码行,对应本地项目app.js文件的第79行第24列。
原有调用代码
app.post("/", function(req, res) { const nameStartsWith = req.body.superhero; const ts = "timestamp"; const apikey = "myPublicKey"; const hash = "md5 digest"; const url = "http://gateway.marvel.com/v1/public/characters?ts=" + ts + "&apikey=" + apikey + "&hash=" + hash + "&nameStartsWith=" + nameStartsWith; http.get(url, function(response) { response.on("data", function(data) { // 此处抛出解析错误 console.log(JSON.parse(data)); }); }); });
异常表现
当前接口返回的内容为截断的不完整JSON片段,示例如下:
{"code":200,"status":"Ok","copyright":"© 2022 MARVEL","attributionText":"Data provided by Marvel. © 2022 MARVEL","attributionHTML":"<a href=\"http://marvel.com\">Data provided by Marvel. © 2022 MARVEL</a>","etag":"08aea880d9e638f37cf6011d63a936fbe3f82f22","data":{"offset":0,"limit":20,"total":5,"count":5,"results":[{"id":1009718,"name":"Wolverine","description":"Born with super-human senses and the power to heal from almost any wound, Wolverine was captured by a secret Canadian organization and given an unbreakable skeleton and claws. Treated like an animal, it took years for him to control himself. Now, he's a premiere member of both the X-Men and the Avengers.","modified":"2016-05-02T12:21:44-0400","thumbnail":{"path":"http://i.annihil.us/u/prod/marvel/i/mg/2/60/537bcaef0f6cf","extension":"jpg"},"resourceURI":"http://gateway.marvel.com/v1/public/characters/1009718","comics":{"available":2549,"collectionURI":"http://gateway.marvel.com/v1/public/characters/1009718/comics","items":[{"resourceURI":"http://gateway.marvel.com/v1/public/comics/41113","name":"5 Ronin (Hardcover)"},{"resourceURI":"http://gateway.marvel.com/v1/public/comics/38756","name":"5 Ronin (2010) #1"
终端输出的完整错误栈:
SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) at IncomingMessage.<anonymous> (C:\Users\mishr\OneDrive\Desktop\MarvelComics-Project\app.js:79:24)
错误原因
Node.js中HTTP响应的data事件是分块触发的:响应数据体积较大时,会被拆分成多个数据块传输,每收到一个块就会触发一次data事件,此时回调里拿到的data只是完整响应的一个片段,不是完整的JSON结构。直接对截断的片段执行JSON.parse,就会抛出“JSON输入意外结束”的语法错误,拿到的返回内容在"5 Ronin (2010) #1"处截断就是最直接的证据。
修复方案
需要先收集所有传输过来的数据块,等所有数据接收完成(触发end事件)后,再把拼接好的完整字符串做JSON解析,修复后的代码如下:
app.post("/", function(req, res) { const nameStartsWith = req.body.superhero; const ts = "timestamp"; const apikey = "myPublicKey"; const hash = "md5 digest"; const url = "http://gateway.marvel.com/v1/public/characters?ts=" + ts + "&apikey=" + apikey + "&hash=" + hash + "&nameStartsWith=" + nameStartsWith; http.get(url, function(response) { // 初始化变量存储所有数据块 let rawData = ''; // 设置编码,自动将Buffer类型的块转为utf8字符串 response.setEncoding('utf8'); // 每收到一个数据块就拼接到rawData中 response.on("data", function(chunk) { rawData += chunk; }); // 所有数据接收完成后再执行解析 response.on("end", function() { try { const apiResult = JSON.parse(rawData); console.log(apiResult); // 后续业务逻辑,比如把结果返回给前端 res.send(apiResult); } catch (parseError) { // 捕获解析异常,避免服务崩溃 console.error("JSON解析失败:", parseError); res.status(500).send("接口数据解析异常"); } }); // 监听请求层面的错误 response.on("error", function(err) { console.error("请求Marvel接口失败:", err); res.status(500).send("接口请求失败"); }) }); });
注意事项
- 永远不要在
data事件回调中直接对响应内容做完整JSON解析,该事件触发时数据大概率还没传输完成 - 解析JSON时建议包裹
try/catch,避免接口返回非JSON格式内容(比如网关报错页、空响应)时导致服务进程崩溃 - 建议同时监听响应的
error事件,处理网络中断、超时等请求层面的异常
内容的提问来源于stack exchange,提问作者Satyam Adarsh
相关产品推荐
相关产品推荐

