Alexa Skill调用第三方API报错:Session ended with reason: ERROR排查
我看你在开发Alexa Skill时调用numbersapi的API,遇到了CloudWatch日志显示Session ended with reason: ERROR的问题,还拿不准是NumberIntentHandler还是httpGet函数出了问题。咱们来拆解下你提供的两段代码的问题,再给出修复方案:
第一个版本(带Async/Promise)的核心问题
你的httpGet是异步函数(返回Promise),但在handle方法里直接用const myResult = httpGet(options);接收结果——这时候myResult只是一个未完成的Promise对象,不是API返回的实际文本。Alexa的handler要么同步返回响应,要么返回一个Promise来处理异步逻辑,你直接把Promise赋值给speechText,必然会导致Alexa无法解析响应,触发Session错误。
另外httpGet里还有个小bug:判断响应状态码时用了response < 200 || response > 299,但这里的response是HTTP响应对象,不是状态码,应该用response.statusCode。
第二个版本(移除Async/Await/Promise)的核心问题
你改成回调模式后,handle方法的逻辑完全走偏了:你把构建响应的代码放在了httpGet的回调里,但Alexa的handle方法需要立即返回响应或返回Promise,而你的代码里handle没有任何返回值——因为httpGet是异步的,回调执行时handle早就结束了,Alexa收不到响应,直接触发ERROR。
修正后的完整代码方案
我们需要让handle方法支持异步逻辑,同时正确处理API调用的成功/失败场景:
修正后的NumberIntentHandler
const NumberIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'NumberIntent'; }, // 将handle声明为async函数,支持await处理异步操作 async handle(handlerInput) { let slotNum = handlerInput.requestEnvelope.request.intent.slots.number.value; const myRequest = parseInt(slotNum); console.log('NumberIntentHandler myRequest: ', myRequest); const options = `http://numbersapi.com/${myRequest}`; console.log('NumberIntentHandler options: ', options); try { // 用await等待httpGet的Promise完成,拿到实际API返回值 const myResult = await httpGet(options); console.log("sent : " + options); console.log("received : " + myResult); const speechText = myResult; console.log('speechText: ', speechText); return handlerInput.responseBuilder .speak(speechText) .withSimpleCard('Here is your fact: ', speechText) .getResponse(); } catch (error) { // 捕获API调用失败的情况,返回友好提示 console.error('API调用失败:', error); const errorSpeech = "抱歉,我暂时无法获取这个数字的相关信息,请稍后再试。"; return handlerInput.responseBuilder .speak(errorSpeech) .withSimpleCard('出错了', errorSpeech) .getResponse(); } }, };
修正后的httpGet函数
const http = require('http'); // 别忘了引入http模块! async function httpGet(options) { console.log(`~~~~~~~~~ httpGet ~~~~~~~~~`); console.log(`~~~~~${JSON.stringify(options)}~~~~~`); return new Promise((resolve, reject) => { const request = http.get(options, (response) => { // 修复状态码判断:使用response.statusCode if (response.statusCode < 200 || response.statusCode > 299) { reject(new Error('加载失败,状态码: ' + response.statusCode)); } const body = []; response.on('data', (chunk) => body.push(chunk)); response.on('end', () => resolve(body.join(''))); }); request.on('error', (err) => reject(err)); request.end(); }); }
关键修复点总结
- 让handle支持异步:把
handle声明为async函数,用await等待API调用完成,确保拿到实际数据再构建响应。 - 添加错误捕获:用
try/catch处理API调用失败的情况,避免Session直接报错,同时给用户友好提示。 - 修复状态码判断:把
response改为response.statusCode,正确识别HTTP响应状态。 - 确保引入http模块:如果你的代码里没加
const http = require('http');,一定要补上,否则会触发模块未找到的错误。
另外建议你去CloudWatch的日志组里查看更详细的错误堆栈——比如如果是Promise未处理、模块缺失这类问题,日志里会有明确的报错信息,能帮你更快定位问题。
内容的提问来源于stack exchange,提问作者Hackbrew

