基于ASP.NET Core Web API与Angular实现Twilio外呼手机的技术问询
管理员呼叫用户手机解决方案(Angular + ASP.NET Core + Twilio)
一、后端ASP.NET Core Web API改造
1. 替换呼叫创建的回调URL
原代码使用的Twilio Demo XML仅作演示,需替换为自定义API端点,用于处理用户接听后的通话桥接逻辑:
string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID"); string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN"); string twilioNumber = Environment.GetEnvironmentVariable("TWILIO_PHONE_NUMBER"); TwilioClient.Init(accountSid, authToken); var call = CallResource.Create( url: new Uri($"{Request.Scheme}://{Request.Host}/api/voice/handle-call"), to: new Twilio.Types.PhoneNumber("用户手机号"), from: new Twilio.Types.PhoneNumber(twilioNumber) ); Console.WriteLine(call.Sid);
2. 实现通话处理API(用户接听后触发)
编写API返回TwiML指令,告知Twilio将用户通话桥接到管理员终端(软电话或手机):
[ApiController] [Route("api/voice")] public class VoiceController : ControllerBase { [HttpGet("handle-call")] public IActionResult HandleIncomingCall() { var response = new Twilio.TwiML.VoiceResponse(); // 桥接到管理员的Twilio Client身份(若用软电话) response.Dial(new Dial { CallerId = Request.Query["From"] }).Client("admin"); // 若管理员用固定手机号,替换为: // response.Dial(new Dial { CallerId = Request.Query["From"] }).Number("管理员手机号"); return Content(response.ToString(), "application/xml"); } }
3. 前端调用的发起呼叫API
暴露接口供Angular触发呼叫请求:
[HttpPost("initiate-call")] public IActionResult InitiateCall([FromBody] CallRequest request) { string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID"); string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN"); string twilioNumber = Environment.GetEnvironmentVariable("TWILIO_PHONE_NUMBER"); TwilioClient.Init(accountSid, authToken); var call = CallResource.Create( url: new Uri($"{Request.Scheme}://{Request.Host}/api/voice/handle-call"), to: new Twilio.Types.PhoneNumber(request.UserPhoneNumber), from: new Twilio.Types.PhoneNumber(twilioNumber) ); return Ok(new { CallSid = call.Sid }); } public class CallRequest { public string UserPhoneNumber { get; set; } }
4. 生成Twilio认证Token的API
为Angular端的软电话生成认证Token:
[HttpGet("get-token")] public IActionResult GetToken(string identity) { string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID"); string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN"); string twilioApiKey = Environment.GetEnvironmentVariable("TWILIO_API_KEY"); string twilioApiSecret = Environment.GetEnvironmentVariable("TWILIO_API_SECRET"); var token = new Twilio.Jwt.AccessToken( accountSid, twilioApiKey, twilioApiSecret, identity: identity ); var grant = new Twilio.Jwt.AccessToken.VoiceGrant { OutgoingApplicationSid = "你的TwiML应用SID", // Twilio控制台创建的TwiML应用SID IncomingAllow = true }; token.AddGrant(grant); return Ok(new { Token = token.ToJwt() }); }
二、Angular前端实现
1. 安装依赖
npm install @twilio/voice-sdk
2. 核心组件代码
实现软电话初始化、呼叫触发、通话控制:
import { Component, OnInit } from '@angular/core'; import { Voice } from '@twilio/voice-sdk'; import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-admin-call', templateUrl: './admin-call.component.html', styleUrls: ['./admin-call.component.css'] }) export class AdminCallComponent implements OnInit { private voiceClient: Voice | null = null; currentCall: any = null; userPhoneNumber = ''; constructor(private http: HttpClient) { } ngOnInit(): void { this.initTwilioClient(); } async initTwilioClient(): Promise<void> { const tokenResp = await this.http.get<{ Token: string }>('/api/voice/get-token?identity=admin').toPromise(); if (tokenResp?.Token) { this.voiceClient = new Voice(tokenResp.Token); // 监听来电(用户接听后Twilio会呼叫管理员软电话) this.voiceClient.on('incoming', (call) => { call.accept(); this.currentCall = call; call.on('disconnect', () => this.currentCall = null); }); } } async initiateCall(): Promise<void> { if (!this.userPhoneNumber || !this.voiceClient) return; await this.http.post('/api/voice/initiate-call', { UserPhoneNumber: this.userPhoneNumber }).toPromise(); } hangUp(): void { this.currentCall?.disconnect(); this.currentCall = null; } toggleMute(): void { this.currentCall?.mute(!this.currentCall.isMuted); } }
3. 组件模板
<div class="call-panel"> <input type="text" [(ngModel)]="userPhoneNumber" placeholder="输入用户手机号" /> <button (click)="initiateCall()" [disabled]="currentCall">发起呼叫</button> <button (click)="hangUp()" [disabled]="!currentCall">挂断</button> <button (click)="toggleMute()" [disabled]="!currentCall"> {{ currentCall?.isMuted ? '取消静音' : '静音' }} </button> <div *ngIf="currentCall" class="call-status">通话中...</div> </div>
三、关键逻辑说明
- 用户接听后处理:Twilio会自动调用
handle-callAPI,返回的TwiML通过<Dial>指令将用户手机与管理员终端桥接,实现双向通话。 - 双向通信核心:管理员端通过Twilio Client SDK实现软电话,依赖后端生成的Token完成Twilio认证,通话由Twilio负责中转。
- 环境变量配置:确保后端配置Twilio的Account SID、Auth Token、API Key、API Secret、Twilio号码、TwiML应用SID。
内容的提问来源于stack exchange,提问作者Suraj
相关产品推荐
相关产品推荐

