You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
    }
};

后续配置注意事项

  1. 去Twilio控制台配置你购买的手机号的Webhook地址,将收到短信时的POST请求地址设置为你的Azure HTTP触发器的公网访问地址
  2. 确保Azure HTTP触发器的访问权限设置为匿名,或者将Twilio的IP段加入允许列表
  3. 检查Azure Function的环境变量是否配置正确:foodKey、foodHost、TWILIO_SID、TWILIO_TOKEN、SENDER_NUMBER、RECIPIENT_NUMBER都要填写对应有效值

内容的提问来源于stack exchange,提问作者Beatrix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 04:18:02