Azure Functions在开发基于触发器、输入绑定和输出绑定的应用程序方面有自己的见解。Azure支持两种使用输出绑定的Twilio产品。用于发送短信的Twilio Programmable Messaging和用于发送电子邮件的Twilio SendGrid。
在本教程中,你将学习如何使用Azure Functions和Twilio绑定,用C# .NET发送文本信息。
前提条件
你需要这些东西才能跟上:
- 支持.NET的操作系统(Windows/Mac/Linux)
- .NET 6 SDK
- Azure Functions核心工具
- 一个代码编辑器或IDE(推荐:带C#插件的VS Code、Visual Studio或JetBrains Rider)
- 一个免费的Twilio账户(如果你在这里注册,当你升级到付费账户时,你会得到10美元的Twilio积分!)。
开始使用Twilio
在开发Azure功能之前,你需要对Twilio进行一些设置:
- 如果你还没有Twilio的电话号码,就去向Twilio购买一个新的电话号码。电话号码的费用将被应用于你的免费促销信用。
请确保记下你的新Twilio电话号码。你以后会需要它的! - 如果你使用的是Twilio的试用账户,你只能向经过验证的来电号码发送短信。如果你的电话号码或你想发短信的电话号码不在验证过的来电号码列表中,请验证一下。
- 最后,你需要找到你的Twilio账户SID和Auth Token。浏览你的Twilio账户页面,注意位于页面左下方的Twilio账户SID和Auth Token。

准备你的本地Azure Functions环境
你需要根据你要开发的Azure Functions的类型来准备你的开发机器。基于HTTP触发器的函数不需要任何额外的步骤,但基于定时器触发器的函数和其他类型的Azure函数则取决于Azure存储。幸运的是,Azure已经开发了一个跨平台和开源的存储模拟器,叫做Azurite。在本地开发Azure函数之前,你需要安装和运行Azurite。
有多种方法来安装Azurite,但最简单的方法之一是将其作为一个全局NPM包来安装。这意味着你的机器上需要安装Node.js的8.0版或更高版本。
要把Azurite安装成一个全局NPM包,请运行以下命令:
npm install -g azurite
当你运行Azurite时,它将在当前目录下创建几个文件和文件夹。创建一个新的目录并导航到它,以避免意外地污染你的当前目录:
mkdir Azurite
cd Azurite
使用以下命令运行Azurite:
azurite
让Azurite在你当前的shell中运行,并为以后的所有命令打开一个新的shell。确保新的shell不是在 文件夹内打开,文件夹内。
创建一个Azure函数
是时候开始开发你的Azure函数了。在上一步打开的新shell中运行以下命令:
func init AzureFunctionsWithTwilioBindings --dotnet
这个命令将创建一个新的文件夹AzureFunctionsWithTwilioBindings,并为Azure函数生成一个.NET项目。
导航到这个新文件夹:
cd AzureFunctionsWithTwilioBindings
这个项目还不包含任何函数。运行以下命令,生成一个基于定时器触发器的Azure函数:
func new --name SendSmsTimer --template "Timer trigger"
这将创建一个新的C#文件。SendSmsTimer.cs。
打开SendSmsTimer.cs文件,将 "0 */5 "改为 "*/15 "。
现在SendSmsTimer.cs文件看起来应该是这样的:
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
namespace SampleAzureFunctions
{
public class SendSmsTimer
{
[FunctionName("SendSmsTimer")]
public void Run([TimerTrigger("*/15 * * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
}
FunctionName 属性标志着该方法是一个Azure函数。通过在方法的参数中使用TimerTrigger 属性,这个方法将在一个时间安排上被触发。传入TimerTrigger 的参数是一个CRON表达式,用于指示Azure Functions平台何时运行该函数。这个特定的CRON表达式每15秒发生一次。
Azure Functions平台使用6部分CRON语法:
* * * * * *
- - - - - -
| | | | | |
| | | | | +--- day of week (0 - 6) (Sunday=0)
| | | | +----- month (1 - 12)
| | | +------- day of month (1 - 31)
| | +--------- hour (0 - 23)
| +----------- min (0 - 59)
+------------- sec (0 - 59)
Azure Functions使用NCrontab库来解析CRON表达式。你可以使用我做的这个方便的在线CRON表达式验证工具来验证你的Azure Functions何时会根据你的CRON表达式运行。
计时器的细节是通过myTimer 参数传入方法的。myTimer 参数是一个输入绑定的例子。这是一个非常简单的输入绑定,但输入绑定的功能可以更强大。Azure Functions平台将一个记录器注入方法中,因为它被指定为方法的第二个参数。参数的顺序其实并不重要,因为平台会使用反射和依赖注入的组合来配置你的Azure Function并注入必要的参数。
该方法本身目前只做一件事:记录一条带有当前日期和时间的消息。
运行下面的命令来运行Azure Functions项目:
func start
由于这是一个.NET项目,你可以使用所有常见的.NET CLI命令,如clean,restore,build 等,但你不能直接运行该项目。你需要使用Azure Functions CLI来运行该项目,如上图所示。
运行Azure Functions项目的输出应该是这样的:
Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore...
All projects are up-to-date for restore.
SampleAzureFunctions -> /Users/nswimberghe/SampleAzureFunctions/bin/output/SampleAzureFunctions.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:04.22
Azure Functions Core Tools
Core Tools Version: 4.0.3971 Commit hash: d0775d487c93ebd49e9c1166d5c3c01f3c76eaaf (64-bit)
Function Runtime Version: 4.0.1.16815
[2022-01-07T21:10:05.720Z] Found /Users/nswimberghe/SampleAzureFunctions/SampleAzureFunctions.csproj. Using for user secrets file configuration.
Functions:
SendSmsTimer: timerTrigger
For detailed output, run func with --verbose flag.
[2022-01-07T21:10:12.070Z] Host lock lease acquired by instance ID '0000000000000000000000002511DF13'.
[2022-01-07T21:10:15.048Z] Executing 'SendSmsTimer' (Reason='Timer fired at 2022-01-07T16:10:15.0135530-05:00', Id=4098e2d1-5d27-4e5f-bd6f-540eeb3e9d67)
[2022-01-07T21:10:15.059Z] C# Timer trigger function executed at: 1/7/2022 4:10:15 PM
[2022-01-07T21:10:15.083Z] Executed 'SendSmsTimer' (Succeeded, Id=4098e2d1-5d27-4e5f-bd6f-540eeb3e9d67, Duration=54ms)
[2022-01-07T21:10:30.004Z] Executing 'SendSmsTimer' (Reason='Timer fired at 2022-01-07T16:10:30.0032640-05:00', Id=10e74f2c-4e6f-4117-bbba-118a6b595591)
[2022-01-07T21:10:30.004Z] C# Timer trigger function executed at: 1/7/2022 4:10:30 PM
[2022-01-07T21:10:30.005Z] Executed 'SendSmsTimer' (Succeeded, Id=10e74f2c-4e6f-4117-bbba-118a6b595591, Duration=1ms)
每隔15秒,应该会记录下一条类似这样的信息。C# Timer trigger function executed at: 1/7/2022 1:25:00 PM.
在Windows/Linux上按control + c ,或在macOS上按command + c ,就可以停止运行该项目。
现在有了Azure函数,就可以开始整合Twilio消息了。
将Twilio消息集成到你的Azure函数中
Azure函数使用的是触发器、输入和输出绑定的概念。你已经遇到了一个触发器,TimerTrigger ,以及一个类型为TimerInfo 的输入绑定。现在你要使用你的第一个输出绑定,使用Twilio绑定发送文本信息。
使用.NET CLI添加Twilio binding NuGet包:
dotnet add package Microsoft.Azure.WebJobs.Extensions.Twilio
这个库是由微软维护的,但这个库本身依赖于Twilio维护的C# SDK。
用以下代码更新SendSmsTimer.cs:
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;
namespace AzureFunctionsWithTwilioBindings
{
public class SendSmsTimer
{
[FunctionName("SendSmsTimer")]
[return: TwilioSms(
AccountSidSetting = "TwilioAccountSid",
AuthTokenSetting = "TwilioAuthToken"
)]
public CreateMessageOptions Run([TimerTrigger("*/15 * * * * *")] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"SendSmsTimer executed at: {DateTime.Now}");
string toPhoneNumber = Environment.GetEnvironmentVariable("ToPhoneNumber", EnvironmentVariableTarget.Process);
string fromPhoneNumber = Environment.GetEnvironmentVariable("FromPhoneNumber", EnvironmentVariableTarget.Process);
var message = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
{
From = new PhoneNumber(fromPhoneNumber),
Body = "Hello from SendSmsTimer!"
};
return message;
}
}
}
有了这些变化,Run 方法现在将从环境变量中检索收件人和发件人的电话号码,然后创建一个CreateMessageOptions 的实例,然后返回。CreateMessageOptions 让你指定短信应该从哪个电话号码发送,应该发送到哪个电话号码,以及短信的内容是什么。然而,创建这个对象并不能真正发送短信。
Azure Functions平台将调用这个方法,当它收到CreateMessageOptions 实例时,平台将发送短信。为了使平台能够发送短信,它需要对你的Twilio账户进行认证。这个过程由TwilioSms 属性完成,从第13到16行。这个属性适用于返回类型,因此在属性前面有return 这个关键词。AccountSidSetting 和AuthTokenSetting 参数用于指定持有Twilio账户SID和Twilio Auth Token的应用程序设置的名称。
有了这些信息,Twilio绑定将能够连接到Twilio API并发送你的文本消息。
现在,你需要配置ToPhoneNumber 和FromPhoneNumber 环境变量,以及TwilioAccountSid 和TwilioAuthToken 应用程序设置。要做到这一点,打开local.settings.json文件,像下面这样把所有4个设置添加到Values 对象中:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"TwilioAccountSid": "[REPLACE_WITH_YOUR_TWILIO_ACCOUNT_SID]",
"TwilioAuthToken": "[REPLACE_WITH_YOUR_TWILIO_AUTH_TOKEN]",
"FromPhoneNumber": "[REPLACE_WITH_YOUR_TWILIO_PHONE_NUMBER]",
"ToPhoneNumber": "[REPLACE_WITH_RECIPIENT_PHONE_NUMBER]"
}
}
替换以下字符串并保存文件:
- [REPLACE_WITH_YOUR_TWILIO_ACCOUNT_SID] 用你之前注意到的Twilio账户SID。
- [REPLACE_WITH_YOUR_TWILIO_AUTH_TOKEN] 用你先前注意到的Twilio账户授权令牌。
- [REPLACE_WITH_YOUR_TWILIO_PHONE_NUMBER] 用你先前购买的Twilio电话号码。
- [REPLACE_WITH_RECIPIENT_PHONE_NUMBER] 用你想发送短信的电话号码。如果你使用的是试用账户,这个电话号码必须是前面提到的经过验证的来电显示。
这些设置是敏感的,所以要非常小心,不要把它们分享或检查到git中,特别是Auth token。Git默认会忽略local.settings.json文件,不过还是要小心。
JSON文件中的值将被视为应用程序设置,也可作为环境变量使用。
启动Azure Function项目以测试该项目:
func start
Azure函数应该每15秒被调用一次,它将使用Twilio发送一条短信。
但如果你已经有了另一个输出绑定作为返回值怎么办?或者如果你想发送两条不同的信息呢?
作为替代返回CreateMessageOptions 的实例,你也可以在你的方法中添加out 的参数。
这里有一个更新的例子:
[FunctionName("SendSmsTimer")]
public void Run(
[TimerTrigger("*/15 * * * * *")]TimerInfo myTimer,
ILogger log,
[TwilioSms(AccountSidSetting = "TwilioAccountSid",AuthTokenSetting = "TwilioAuthToken")]
out CreateMessageOptions message1,
[TwilioSms(AccountSidSetting = "TwilioAccountSid",AuthTokenSetting = "TwilioAuthToken")]
out CreateMessageOptions message2
)
{
log.LogInformation($"SendSmsTimerOut executed at: {DateTime.Now}");
string toPhoneNumber = Environment.GetEnvironmentVariable("ToPhoneNumber", EnvironmentVariableTarget.Process);
string fromPhoneNumber = Environment.GetEnvironmentVariable("FromPhoneNumber", EnvironmentVariableTarget.Process);
message1 = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
{
From = new PhoneNumber(fromPhoneNumber),
Body = "Hello from SendSmsTimerOut message 1!"
};
message2 = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
{
From = new PhoneNumber(fromPhoneNumber),
Body = "Hello from SendSmsTimerOut message 2!"
};
}
现在不是将TwilioSms 属性应用于方法的返回类型,而是将该属性应用于两个out CreateMessageOptions 参数。只需将你的CreateMessageOptions 实例分配给你的out 参数,平台将接收输出并发送文本信息。
我如何在不指定多个out 参数的情况下发送多个消息?
另外,你可以使用通用的ICollector<T> 类型和IAsyncCollector<T> 类型作为你方法的参数。平台将注入一个指定类型的实例,你可以向其添加任何数量的你指定的通用类型的实例。这里有一个例子:
[FunctionName("SendSmsTimer")]
public void Run(
[TimerTrigger("*/15 * * * * *")]TimerInfo myTimer,
ILogger log,
[TwilioSms(AccountSidSetting = "TwilioAccountSid",AuthTokenSetting = "TwilioAuthToken")]
ICollector<CreateMessageOptions> messageCollector
)
{
log.LogInformation($"SendMultilpeSmsTimer executed at: {DateTime.Now}");
string toPhoneNumber = Environment.GetEnvironmentVariable("ToPhoneNumber", EnvironmentVariableTarget.Process);
string fromPhoneNumber = Environment.GetEnvironmentVariable("FromPhoneNumber", EnvironmentVariableTarget.Process);
for (int i = 1; i <= 2; i++)
{
var message = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
{
From = new PhoneNumber(fromPhoneNumber),
Body = $"Hello from SendMultilpeSmsTimer #{i}!"
};
messageCollector.Add(message);
}
}
现在该方法接受一个ICollector<CreateMessageOptions> 的实例,你可以向其添加多个CreateMessageOptions 实例。for循环将添加两个CreateMessageOptions 实例,当使用messageCollector.Add 添加一个实例时,平台将发送文本信息。
另外,你可以使用IAsyncCollector ,而不是ICollector 。异步版本不会立即发送文本信息,而是等待FlushAsync 被调用。如果FlushAsync 没有被调用,平台将在该方法完成后发送你的文本信息。
这里是异步版本:
public async Task Run(
[TimerTrigger("*/15 * * * * *")]TimerInfo myTimer,
ILogger log,
[TwilioSms(AccountSidSetting = "TwilioAccountSid",AuthTokenSetting = "TwilioAuthToken")]
IAsyncCollector<CreateMessageOptions> messageCollector
)
{
log.LogInformation($"SendMultilpeAsyncSmsTimer executed at: {DateTime.Now}");
string toPhoneNumber = Environment.GetEnvironmentVariable("ToPhoneNumber", EnvironmentVariableTarget.Process);
string fromPhoneNumber = Environment.GetEnvironmentVariable("FromPhoneNumber", EnvironmentVariableTarget.Process);
for (int i = 1; i <= 2; i++)
{
var message = new CreateMessageOptions(new PhoneNumber(toPhoneNumber))
{
From = new PhoneNumber(fromPhoneNumber),
Body = $"Hello from SendMultilpeAsyncSmsTimer #{i}!"
};
await messageCollector.AddAsync(message);
}
await messageCollector.FlushAsync();
}
这些就是你可以使用Azure Functions的Twilio绑定来发送短信的所有方法。Twilio绑定是一个有用的功能,但如果它不能满足你的要求,你仍然可以像其他.NET应用程序一样直接使用Twilio C# SDK。
总结
Azure Functions使用触发器、输入和输出绑定的概念。微软为Azure Functions维护了Twilio绑定,这有助于你使用Twilio平台发送文本信息。有多种方法可以使用Azure Functions来发送文本信息:
- 返回一个
CreateMessageOptions实例 - 将一个
CreateMessageOptions实例分配给一个out参数 - 让平台注入一个
ICollector<CreateMessageOptions>或IAsyncCollector<CreateMessageOptions>的实例,并向其添加CreateMessageOptions的实例 - 使用Twilio C# SDK