Node.js调用Twitch接口校验用户名解析body时触发TypeError报错
报错根因
- 未声明请求方法:
request库默认发送GET请求,而Twitch GQL接口仅接受POST请求,接口返回的根本不是预期的GQL响应数组,直接读取body[0]得到undefined,再往下读取data属性就会抛出类型错误。 - 无错误兜底逻辑:没有处理请求报错、HTTP非200状态、JSON解析失败、字段不存在的场景,只要任意一步不符合预期就会直接抛错。
- 异步逻辑写法错误:在
request的异步回调里return值,外层getUser函数是同步执行逻辑,根本拿不到回调里的返回结果。 - 请求体构造不规范:手动拼接JSON字符串,一旦用户名包含引号等特殊字符,会直接破坏请求体JSON结构,导致接口返回错误。
- 业务判断逻辑错误:Twitch接口对不存在的用户名返回的
data.user值为null,并非id等于0,原判断条件永远不会触发无效用户名的分支。
修复后代码
// 注:request库已停止维护,后续可替换为undici、axios、node-fetch等现代请求库,以下代码基于你现有依赖修改 function getUser(username) { return new Promise((resolve, reject) => { const opts = { url: "https://gql.twitch.tv/gql", method: "POST", // 补全必须的POST请求方法 headers: { 'Connection': 'keep-alive', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"', 'Accept-Language': 'en-US', 'sec-ch-ua-mobile': '?0', 'Client-Version': '7b9843d8-1916-4c86-aeb3-7850e2896464', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36', 'Content-Type': 'application/json', // 替换为标准JSON请求头 'Client-Session-Id': '51789c1a5bf92c65', 'Client-Id': 'kimne78kx3ncx6brgo4mv6wki5h1ko', 'X-Device-Id': 'xH9DusxeZ5JEV7wvmL8ODHLkDcg08Hgr', 'sec-ch-ua-platform': '"Windows"', 'Accept': '*/*', 'Origin': 'https://www.twitch.tv', 'Sec-Fetch-Site': 'same-site', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Dest': 'empty', 'Referer': 'https://www.twitch.tv/', }, // 用JSON.stringify序列化请求体,避免手动拼接字符串导致的结构错误 body: JSON.stringify([{ operationName: "WatchTrackQuery", variables: { channelLogin: username, videoID: null, hasVideoID: false }, extensions: { persistedQuery: { version: 1, sha256Hash: "38bbbbd9ae2e0150f335e208b05cf09978e542b464a78c2d4952673cd02ea42b" } } }]) } request(opts, (err, res, body) => { // 先处理请求层错误 if (err) return reject(new Error(`请求失败: ${err.message}`)); if (res.statusCode !== 200) return reject(new Error(`接口异常,状态码: ${res.statusCode}`)); // 加JSON解析容错,避免接口返回非JSON内容时报错 let parsedRes; try { parsedRes = JSON.parse(body); } catch (parseErr) { return reject(new Error(`响应解析失败: ${parseErr.message}`)); } // 用可选链逐层读取字段,避免undefined读属性的报错 const userId = parsedRes?.[0]?.data?.user?.id; if (!userId) { resolve("Invalid Username"); } else { resolve(userId); } }); }); } // 调用示例(异步函数内可加await使用) // getUser("目标用户名") // .then(result => console.log("校验结果:", result)) // .catch(error => console.error("校验出错:", error))
注意事项
- 异步函数必须通过Promise、回调或者async/await的方式获取结果,不要试图在异步回调里return给外层同步函数拿值。
- 所有外部接口返回的数据都必须做存在性校验,不要默认接口永远返回你预期的结构,网络波动、接口变更、参数错误都可能导致返回结构变化。
- 构造JSON格式的请求体时,永远用
JSON.stringify方法序列化,不要手动拼接字符串,避免特殊字符转义问题。
内容的提问来源于stack exchange,提问作者R3D
相关产品推荐
相关产品推荐

