原生NodeJS开发Appsync客户端执行queries/mutations时偶发JSON解析错误
问题根因
- 最核心的问题是你对HTTP响应的流式处理逻辑错误:原生
https模块返回的IncomingMessage对象的data事件会在每收到一段数据分片时触发,并非等待全部响应加载完成后才触发。当网络波动、响应体体积较大时,完整响应会被拆成多个分片返回,你直接对单个分片做JSON解析自然会偶发格式错误。 - 还有两个隐性错误会加剧问题:
- 监听的响应错误事件名错误,正确事件名是
error不是errors,运行过程中的响应层错误无法被正常捕获,会直接抛出未处理异常 throwOnErrors函数要求接收一个包含query/variables/errors字段的对象参数,你当前调用时传入的是三个零散参数,会导致错误发生时无法正常生成错误信息
- 监听的响应错误事件名错误,正确事件名是
修复方案
你需要先累加所有响应分片,等全部响应加载完成后再做JSON解析,同时修正上述隐性问题,修复后的参考代码如下:
var https = require("https"); let AWS = require("aws-sdk"); let urlParse = require("url").URL; require("dotenv").config(); const throwOnErrors = ({ query, variables, errors }) => { if (errors) { const errorMessage = ` query: ${query.substr(0, 100)} variables: ${JSON.stringify(variables, null, 4)} error: ${JSON.stringify(errors, null, 4)} `; throw new Error(errorMessage); } }; module.exports = async ( url, credentials, query, operationName, variables = {} ) => { AWS.config.update({ region: process.env.AWS_REGION, credentials: new AWS.Credentials( credentials.accessKeyId, credentials.secretAccessKey, credentials.sessionToken ), }); let endpoint = new urlParse(url).hostname.toString(); const item = { input: variables }; let req = new AWS.HttpRequest(url, process.env.AWS_REGION); req.method = "POST"; req.headers.host = endpoint; req.headers["Content-Type"] = "application/json"; req.body = JSON.stringify({ query: query, operationName: operationName, variables: item }); let signer = new AWS.Signers.V4(req, "appsync", true); signer.addAuthorization(AWS.config.credentials, AWS.util.date.getDate()); const data = await new Promise((resolve, reject) => { const httpRequest = https.request({ ...req, host: endpoint }, (result) => { // 声明buffer累加响应分片 let chunks = []; // 累加分片 result.on("data", (chunk) => { chunks.push(chunk); }); // 全部响应接收完成后处理 result.on("end", () => { try { const responseStr = Buffer.concat(chunks).toString(); // 可选:先打印responseStr排查非JSON响应的场景,比如网关报错返回的HTML const resultdata = JSON.parse(responseStr); if (resultdata.errors) { // 修正传参格式 throwOnErrors({ query, variables, errors: resultdata.errors }); reject(resultdata); } else { resolve(resultdata); } } catch (e) { // 捕获解析错误,避免直接抛异常 reject(e); } }); // 修正错误事件名 result.on("error", (err) => { reject(err); }); }); // 监听请求层错误 httpRequest.on("error", (err) => { reject(err); }); httpRequest.write(req.body); httpRequest.end(); }); return data.data };
如果修复后仍然偶发解析错误,可以在JSON.parse前打印完整的响应字符串,大概率是网关/权限校验失败返回了非JSON格式的错误报文(比如HTML格式的403/503错误页),可以针对性排查签名、权限配置问题。
内容的提问来源于stack exchange,提问作者Gabriele Biagini
相关产品推荐
相关产品推荐

