Azure Function结合Twilio实现SMS食谱查询功能故障排查求助
问题排查与修复方案
核心错误点
- HTTP触发器中错误嵌入了Express服务启动逻辑:Azure Function为无服务运行环境,无需自行监听端口,该逻辑会导致端口占用、函数执行超时。
- fetch请求未正确处理返回值:你在then回调中仅打印了响应对象,未返回json格式的业务数据,导致食谱数据始终为undefined。
- 异步逻辑处理混乱:await使用位置错误,且定时器函数中提前调用context.done()会中断后续异步逻辑执行。
- 未正确配置Twilio webhook对接逻辑:Twilio用户回复的短信会直接向你的HTTP触发器URL发起POST请求,无需自行搭建Express路由接收。
修复后代码
HTTP触发函数代码
const fetch = require('node-fetch'); const MessagingResponse = require('twilio').twiml.MessagingResponse; module.exports = async function (context, req) { context.log('JavaScript HTTP trigger function processed a request.'); const twiml = new MessagingResponse(); try { // 解析Twilio发来的用户短信内容 const userInput = req.body?.Body?.trim(); if (!userInput) { twiml.message("Oops, 你没有输入任何食材哦,试试回复面包、大米等食材名称获取对应食谱~"); context.res = { status: 200, headers: { 'Content-Type': 'text/xml' }, body: twiml.toString() }; return context.done(); } // 调用美食API获取食谱 const apiResp = await fetch(`https://spoonacular-recipe-food-nutrition-v1.p.rapidapi.com/recipes/findByIngredients?ingredients=${encodeURIComponent(userInput)}&number=1&ignorePantry=true&ranking=1`, { method: "GET", headers: { "x-rapidapi-key": process.env.foodKey, "x-rapidapi-host": process.env.foodHost } }); const recipeList = await apiResp.json(); if (!recipeList || recipeList.length === 0) { twiml.message("抱歉,没有找到对应食材的食谱,试试其他食材吧~"); } else { const recipe = recipeList[0]; twiml.message(`为你找到的食谱:${recipe.title}\n需要的食材:${recipe.usedIngredients.map(item => item.name).join('、')}\n查看详情:${recipe.sourceUrl}`); } // 返回Twilio要求的XML格式响应 context.res = { status: 200, headers: { 'Content-Type': 'text/xml' }, body: twiml.toString() }; context.done(); } catch (err) { context.log.error("处理出错:", err); twiml.message("服务暂时出现问题,请稍后再试~"); context.res = { status: 200, headers: { 'Content-Type': 'text/xml' }, body: twiml.toString() }; context.done(); } };
定时器触发函数代码
const client = require('twilio')(process.env.TWILIO_SID, process.env.TWILIO_TOKEN); module.exports = async function (context, myTimer) { var timeStamp = new Date().toISOString(); if (myTimer.IsPastDue) { context.log('JavaScript is running late!'); } try { // 发送早餐提醒短信 await client.messages.create({ from: process.env.SENDER_NUMBER, body: "Morning! Time to have breakfast 😋🥘. What ingredient would you like to have?", to: process.env.RECIPIENT_NUMBER, mediaUrl: "https://miro.medium.com/max/700/1*OtRzMDGD0qepGUCToZHZ3Q.jpeg", }); context.log("提醒短信发送成功", timeStamp); context.res = { body: 'Text successfully sent' }; context.done(); } catch (err) { context.log.error("Twilio Error: ", err); context.res = { status: 500, body: `Twilio Error Message: ${err.message}\nTwilio Error code: ${err.code}` }; context.done(); } };
后续配置注意事项
- 去Twilio控制台配置你购买的手机号的Webhook地址,将收到短信时的POST请求地址设置为你的Azure HTTP触发器的公网访问地址
- 确保Azure HTTP触发器的访问权限设置为匿名,或者将Twilio的IP段加入允许列表
- 检查Azure Function的环境变量是否配置正确:
foodKey、foodHost、TWILIO_SID、TWILIO_TOKEN、SENDER_NUMBER、RECIPIENT_NUMBER都要填写对应有效值
内容的提问来源于stack exchange,提问作者Beatrix
相关产品推荐
相关产品推荐

