Node.js调用Unsplash API报Unexpected end of JSON input错误
错误根因
Node.js原生https模块返回的响应是可读流,当接口返回数据量较大时,data事件会被多次触发,每次仅返回响应内容的一个数据分片。你现在的逻辑是每次收到分片就直接调用JSON.parse,此时拿到的只是截断的不完整JSON片段——从你贴的报错日志就能看出来,返回内容最后断在https://s3.us-west-2.amazonaws.com/images.unsplash.com/small/pho位置,JSON结构根本没闭合,必然报Unexpected end of JSON input错误。
除此之外你的代码还有两个明显问题:
try/catch写在data事件监听器的外层,根本捕获不到异步回调里抛出的错误,所以报错直接打挂了Node进程- 数据还没接收完就调用
res.send(),客户端根本拿不到完整的返回内容
修复代码
正确的流处理逻辑是先把所有分片拼接缓存,等响应触发end事件(代表所有数据传输完成)之后,再统一做JSON解析,修正后的完整代码如下:
const express = require("express"); const https = require("https"); const app = express(); const port = 4000; app.use(express.static("public")); app.set("view engine", "ejs"); app.get("/", (req, res) => { const unsplashUrl = "https://api.unsplash.com/photos/?client_id=ipLjiRmWcJ-jWn5uG8UhibNGiFgHxTVE_KeHHb8Oo3M"; https.get(unsplashUrl, (apiRes) => { const { statusCode } = apiRes; if (statusCode !== 200) { return res.status(500).send(`Unsplash接口请求失败,状态码:${statusCode}`); } // 缓存所有数据分片 let responseBuffer = ""; // 逐块接收数据 apiRes.on("data", (chunk) => { responseBuffer += chunk.toString(); }); // 数据全部接收完成后再解析 apiRes.on("end", () => { try { const photoData = JSON.parse(responseBuffer); console.log("获取到Unsplash图片数据:", photoData); // 业务逻辑:可以把数据传给ejs模板渲染,也可以直接返回json res.render("index", { photos: photoData }); } catch (parseErr) { console.error("JSON解析失败:", parseErr); res.status(500).send("返回数据解析异常"); } }); // 捕获请求过程中的异常 apiRes.on("error", (reqErr) => { console.error("请求Unsplash接口失败:", reqErr); res.status(500).send("接口请求异常"); }); }); }); app.listen(port, () => console.log(`服务已启动,监听端口:${port}`));
核心修改点
- 新增缓存变量拼接所有收到的数据分片,不在
data事件触发时直接解析JSON - 监听流的
end事件,确认数据完全传输完毕后再执行解析操作 - 把
try/catch放到end事件的回调内部,正确捕获异步场景下的解析错误 - 等数据处理完成后再给客户端返回响应,避免提前断开连接
- 补充了请求异常、状态码异常的处理逻辑,避免单接口报错直接打挂整个服务
内容的提问来源于stack exchange,提问作者Muslim shah
相关产品推荐
相关产品推荐

