Node.js调用YouTube API时JSON.parse解析数据致应用崩溃原因排查
问题
开发带搜索框的前端时,通过Node.js的https.get()调用YouTube API搜索视频,在response.on('data')回调里用JSON.parse()解析返回数据时应用崩溃,报错:
undefined:25 "url": "https://i.ytimg.com/vi/fWRISvAygU/ SyntaxError: Unexpected end of JSON input
同时控制台提示[nodemon] app crashed - waiting for file changes before starting...
已确认以下信息:
- 相同URL在Postman中能获取完整JSON数据;
- 前端传入的参数可正确拼接进请求URL;
https.get()返回状态码为200 OK;- 未解析的data为Buffer数据,且分为5个块(此前调用天气API时是单个Buffer块)。
相关代码:
const express = require("express"); const bodyParser = require("body-parser"); const https = require("https"); const Joi = require("joi"); const apiKey = "MyKeyWouldBeHere"; // 已隐藏密钥 const app = express(); app.use(bodyParser.urlencoded({extended:true})); app.use(express.static("public")); app.get("/", (req, res) => { res.sendFile(`${__dirname}/index.html`); }); app.post("/videos", (req, res) => { const {error} = validation(req.body); if (error) return res.status(400).send("wrong way to write"); const userInput = req.body.userInput; https.get(`https://www.googleapis.com/youtube/v3/search?part=snippet&key=${apiKey}&q=${userInput}`, (response) => { console.log(response.statusCode); response.on("data", function(data) { const weatherData = JSON.parse(data); console.log(data); }); }); }); app.listen("3000", () => { console.log("Running server on port 3000"); }); function validation(value) { const schema = Joi.object({ userInput: Joi.string().min(2).required() }); return schema.validate(value); }
解决方案
问题原因
https.get()的data事件是分块触发的,每次回调拿到的只是完整响应的一部分Buffer。直接对单个块执行JSON.parse(),相当于解析不完整的JSON片段,必然会抛出“Unexpected end of JSON input”错误。天气API返回数据量小,刚好在一个块里,所以之前没出问题,但YouTube API返回的数据更大,会被拆分成多个块传输。
修复步骤
- 声明变量拼接所有接收的Buffer块;
- 监听
response的end事件(所有数据接收完成时触发),统一解析完整的JSON; - 捕获解析错误,避免应用崩溃;
- 将处理后的数据返回给前端。
修复后的代码:
const express = require("express"); const bodyParser = require("body-parser"); const https = require("https"); const Joi = require("joi"); const apiKey = "MyKeyWouldBeHere"; // 已隐藏密钥 const app = express(); app.use(bodyParser.urlencoded({extended:true})); app.use(express.static("public")); app.get("/", (req, res) => { res.sendFile(`${__dirname}/index.html`); }); app.post("/videos", (req, res) => { const {error} = validation(req.body); if (error) return res.status(400).send("wrong way to write"); const userInput = req.body.userInput; https.get(`https://www.googleapis.com/youtube/v3/search?part=snippet&key=${apiKey}&q=${encodeURIComponent(userInput)}`, (response) => { console.log(response.statusCode); // 初始化变量存储所有数据块 let rawData = []; // 分块接收数据,存入数组 response.on("data", (chunk) => { rawData.push(chunk); }); // 所有数据接收完成后处理 response.on("end", () => { try { // 拼接所有Buffer块并解析为JSON const videoData = JSON.parse(Buffer.concat(rawData).toString()); console.log(videoData); // 将数据返回给前端 res.json(videoData); } catch (err) { // 捕获解析错误,避免应用崩溃 console.error("JSON解析错误:", err.message); res.status(500).send("数据解析失败"); } }); // 监听请求错误 response.on("error", (err) => { console.error("请求错误:", err.message); res.status(500).send("请求API失败"); }); }); }); app.listen("3000", () => { console.log("Running server on port 3000"); }); function validation(value) { const schema = Joi.object({ userInput: Joi.string().min(2).required() }); return schema.validate(value); }
额外优化点
- 对
userInput做encodeURIComponent编码,避免特殊字符导致请求URL无效; - 若想简化代码,可使用
axios等HTTP库,它会自动处理分块数据和JSON解析,无需手动拼接Buffer。
内容的提问来源于stack exchange,提问作者LordCail
相关产品推荐
相关产品推荐

