Twilio呼叫接通后自动挂断?客户端提示应用错误排查
Twilio Voice Calling功能异常问题排查
问题描述
我正在搭建Twilio Voice Calling功能,客户端与服务端代码如下,但出现两个异常:
- 客户端收到语音提示:"Sorry, an application error has occurred. Goodbye."
- 被叫方手机响铃并播放TwiML文本后自动挂断。
客户端代码
function makeCall() { if (!window.twilioDevice) { console.error('Twilio Device not ready'); return; } const params = { to: '+1555#######', from: '+1555#######' }; const connection = window.twilioDevice.connect(params); }
服务端代码
WebApp.handlers.use('/twilio_voice', async (req, res, next) => { try { const accountSid = Meteor.settings.twilio.accountSid; const authToken = Meteor.settings.twilio.authToken; const twilioClient = require('twilio')(accountSid, authToken); const to = '+1##########' const from = '+1##########' // const { to, from } = req.body; // Ensure your client sends these in the request body if (!to || !from) { let errorMsg = `twilio_voice endpoint: Missing "to" or "from" phone number. to: ${to} from: ${from}` console.log(errorMsg); res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: errorMsg })); return; } const call = await twilioClient.calls.create({ twiml: '<Response><Say>Hello, this is a call from a user. User, please go ahead and say hello.</Say></Response>', to: to, from: from }); console.log('Call SID: ', call.sid); res.type('application/json'); res.send({ success: true, callSid: call.sid }); } catch (error) { console.error('Error in /twilio_voice:', error); res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: error.message })); } });
问题原因及修复方案
1. 客户端流程错误:未正确初始化Twilio Device
你的客户端直接调用twilioDevice.connect(),但Twilio Device需要先通过服务端获取**能力令牌(Capability Token)**完成初始化,且connect的参数是传递给TwiML App的参数,不是直接触发API呼叫。
修复后的客户端代码:
// 初始化Twilio Device async function initTwilioDevice() { const response = await fetch('/get_twilio_token', { method: 'POST' }); const data = await response.json(); window.twilioDevice = new Twilio.Device(data.token, { logLevel: 1 }); // 监听错误与连接事件 window.twilioDevice.on('error', (err) => console.error('Device Error:', err)); window.twilioDevice.on('connect', (conn) => console.log('Call Connected:', conn)); } // 发起呼叫 function makeCall() { if (!window.twilioDevice) { console.error('Twilio Device not ready'); return; } const params = { to: '+1555#######' }; // 仅传递被叫号码到服务端TwiML端点 window.twilioDevice.connect(params); }
2. 服务端逻辑混淆:误用Calls API替代TwiML路由
当前服务端直接调用twilioClient.calls.create()创建呼叫,这与Twilio Device的WebRTC通话流程不匹配。正确流程是:客户端发起connect后,Twilio会向你的TwiML App配置的URL发送请求,服务端需返回TwiML来桥接客户端与被叫方。
修复后的服务端代码需包含两个端点:
端点1:生成Capability Token
WebApp.handlers.use('/get_twilio_token', async (req, res) => { try { const { accountSid, authToken, twimlAppSid } = Meteor.settings.twilio; const twilioClient = require('twilio')(accountSid, authToken); const capability = new twilioClient.jwt.ClientCapability({ accountSid, authToken }); // 允许客户端接收呼叫 capability.addScope(new twilioClient.jwt.ClientCapability.IncomingClientScope('my_client')); // 允许客户端发起呼叫,关联TwiML App capability.addScope(new twilioClient.jwt.ClientCapability.OutgoingClientScope({ applicationSid: twimlAppSid, params: { clientName: 'my_client' } })); res.json({ success: true, token: capability.toJwt() }); } catch (err) { console.error('Token Generation Error:', err); res.status(500).json({ success: false, error: err.message }); } });
端点2:返回TwiML处理呼叫
WebApp.handlers.use('/twilio_voice_twiml', async (req, res) => { try { const { accountSid, authToken, fromNumber } = Meteor.settings.twilio; const twilioClient = require('twilio')(accountSid, authToken); const to = req.query.to || req.body.to; // 获取客户端传递的被叫号码 if (!to) { res.type('text/xml').send('<Response><Say>Missing destination number.</Say></Response>'); return; } // 生成TwiML桥接客户端与被叫方 const twiml = new twilioClient.twiml.VoiceResponse(); const dial = twiml.dial(); dial.number(to); res.type('text/xml').send(twiml.toString()); } catch (err) { console.error('TwiML Generation Error:', err); res.type('text/xml').send('<Response><Say>An error occurred.</Say></Response>'); } });
3. 缺少Twilio控制台关键配置
- 在Twilio控制台创建TwiML App,将Voice配置中的
Request URL设为你的服务端TwiML端点(如https://your-domain.com/twilio_voice_twiml) - 确保
fromNumber是Twilio已验证或购买的号码 - 检查账户余额与权限,确认可发起呼出呼叫
4. 被叫方自动挂断的原因
原代码中的TwiML仅包含<Say>指令,播放完成后呼叫自动终止。使用<Dial>指令桥接双方后,通话会保持直到任意一方挂断。
内容的提问来源于stack exchange,提问作者VikR
相关产品推荐
相关产品推荐

