Webhooks是一种在事件发生时被外部服务通知的方式。不是你向该服务发送HTTP请求,而是该服务向你的公共网络服务发送HTTP请求。这样,你就可以在事件发生时对其进行实时响应。Webhooks也是与Twilio的产品整合的一种常见方式。例如,当你的Twilio电话号码收到一条短信或电话时,Twilio会向你的服务发送一个HTTP请求,并提供详细信息。然后,你的服务会响应指示,指出Twilio应该如何回应该事件。下面是这种交换的图示:

Webhooks的一个共同挑战是,它们只能调用互联网上公开的网络服务,但当你最初开发软件时,你通常是在自己的本地机器上进行,默认情况下是无法通过互联网到达的。那么,你如何在你的本地机器上开发和测试webhooks?答案是:隧道。在这篇文章中,你将学习如何使用 Cloudflare Tunnels 服务,使你的本地网络服务器在互联网上公开访问。然后,您将学习如何使用隧道服务来响应Twilio webhook,以回应一个电话。
本地隧道
你可以在你的本地机器和隧道服务之间创建一个隧道,将本地端口暴露在公共互联网上。有很多工具可以帮助你做到这一点,今天你要学习的是Cloudflare Tunnel或cloudflared(以前叫Argo Tunnel)。
前提条件
你将需要以下东西才能跟上:
- 一台Linux、macOS或Windows机器
- 你选择的开发堆栈(Node.js、PHP、.NET、Java、Python等)。
- 可选的:一个使用Cloudflare DNS作为名称服务器的网站的Cloudflare账户
- 可选的: 一个Twilio账户(如果你在这里注册,当你升级到付费账户时,你会收到10美元的Twilio积分!)。
设置一个本地网络服务器
你可以在任何时候对一个本地端口进行加密,但是如果你的机器上没有任何东西在监听这个端口,你就不会看到任何结果。在这一节中,你将设置一个本地Web服务器来提供静态文件。
通常,webhook的响应是以编程方式创建的,但为了这个演示的目的,你将使用静态文件。
打开你喜欢的外壳,创建一个文件夹,并导航到它:
mkdir MyStaticSite
cd MyStaticSite
在MyStaticSite文件夹中创建一个新文件index.html,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World!</title>
</head>
<body>
Hello World!
</body>
</html>
你需要运行一个静态Web服务器来服务这个HTML文件。每个开发栈都有许多静态服务器。在下面挑选你喜欢的堆栈,并按照说明为当前文件夹提供服务,网址是http://localhost:8080/。
Node.js:运行下面的命令来执行http-server NPM包并运行它:
npx http-server . --port 8080
PHP:你可以通过运行以下命令使用内置的PHP web服务器:
php -S localhost:8080
**.NET:你可以通过运行以下命令来使用内置的PHP网络服务器。**你可以使用dotnet-serve工具来运行一个静态Web服务器。将该工具安装为一个全局的.NET工具:
dotnet tool install --global dotnet-serve
运行该工具为当前目录提供服务:
dotnet serve --port 8080
Python 2:
python -m SimpleHTTPServer 8080
Python 3:
python -m http.server 8080
如果你喜欢的编程语言没有列在上面,请查看这个静态Web服务器命令的列表。
让静态服务器运行,为即将到来的命令打开一个新的外壳。
使用Cloudflare Tunnel对你的本地Web服务器进行加密
你可以使用 Cloudflare Tunnel 客户端,将你的本地端口接入公共互联网。
为此,你需要使用cloudflared CLI 工具。在此下载 cloudflared 命令行工具。
开始对你的本地机器进行隧道连接的最快捷方式是运行这个命令:
cloudflared tunnel --url http://localhost:8080
下面是输出结果应该是这样的:
Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee. If you intend to use Tunnels in production you should use a pre-created named tunnel by following: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
Requesting new quick Tunnel on trycloudflare.com...
+--------------------------------------------------------------------------------------------+
| Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): |
| https://colleagues-ga-eternal-recruitment.trycloudflare.com |
+--------------------------------------------------------------------------------------------+
... Much more!
你可以在打印到控制台的方框内找到你随机生成的地址。使用网络浏览器进入该地址。你会看到,从你的本地网络服务器提供的 HTML 文件现在是在 Cloudflare 给你的地址上公开提供。
您可以使用这个公共地址,并将其配置为您的webhook地址,与您想集成的外部服务相连接。
为您的 Cloudflare 隧道使用一个持久的地址
当您停止隧道命令时,隧道将被销毁。当你再次运行隧道命令时,将创建一个具有新的随机URL的新隧道。这也意味着你必须在你的外部服务中持续更新webhook URL,这可能是一个麻烦。
其中一些隧道服务确实提供持久的URL,作为其付费计划的一部分。但通过Cloudflare Tunnels,如果你使用Cloudflare DNS,你可以免费拥有持久的URL。你可以在Cloudflare DNS中创建一个子域,并将其作为你的持久性隧道URL,而不是使用随机生成的URL。
如果你选择使用一个持久的URL,你需要:
- 创建一个Cloudflare帐户,并添加您的网站
- 验证你是否拥有你的域名
- 使用Cloudflare DNS作为您的域名服务器
如果您还没有为您的网站使用Cloudflare DNS,而且您也不想这样做,请随意跳过本节。你可以继续使用随机生成的地址进行下一节。
使用以下命令登录Cloudflare:
cloudflared tunnel login
这将打开Cloudflare的登录页面。登录后,选择你想用于隧道的域名:

当提示你是否要授权这个域名时,点击授权。
切换回你的外壳,你应该在输出中看到 "你已成功登录"。
通过运行这个命令创建一个新的隧道:
cloudflared tunnel create myfirsttunnel
你创建了一个隧道,名称为myfirsttunnel 。你可以用下面的命令列出你所有的隧道:
cloudflared tunnel list
运行上面的命令,注意你的隧道的ID。
现在,CLI工具已经创建了一个文件夹来存储其配置。在 Windows 上,它位于*%USERPROFILE%\.cloudflared*。在 Linux 和 macOS 上,你可以在~/.cloudflared 找到这个文件夹。在*.cloudflared* 文件夹中创建一个新文件myfirsttunnel.yaml并粘贴以下内容:
url: http://localhost:8080
tunnel: <Tunnel-UUID>
credentials-file: /root/.cloudflared/<Tunnel-UUID>.json
这个配置文件将配置本地隧道监听*http://localhost:8080。*
用你之前注意到的隧道ID 替换<Tunnel-UUID> 。
运行以下命令,指定你想用于公共隧道URL的子域:
cloudflared tunnel route dns myfirsttunnel mysubdomain
在这个命令中,myfirsttunnel 指定隧道的名称,mysubdomain 指定你想使用的子域。
现在一切都配置好了,你可以用这个命令运行你的隧道:
cloudflared tunnel run myfirsttunnel
你的新隧道URL应该是*:https://<你的子域>.<你的域名>.<你的tld>。例如,我的就是mysubdomain.swimburger.net。*
在浏览器中打开你的新隧道URL,以验证是否返回相同的HTML文件。
用Twilio webhooks使用你的隧道
你也可以使用这些隧道来响应Twilio webhooks。在这一节中,你将学习如何使用静态的XML文件来响应一个电话。
在MyStaticSite文件夹中创建一个新文件call.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say voice="alice">Twilio received these instructions from your local machine via Cloudflare Tunnel.</Say>
<Pause length="1" />
<Say voice="alice">Great job!</Say>
</Response>
这个XML文件使用TwiML,也被称为Twilio Markup Language。使用TwiML,你可以提供Twilio应该如何回应来电或短信的指示。上面的TwiML使用了两个TwiML语音动词:Say 和Pause 。Say 将文本转换为语音,而voice 属性指定了要使用的语音。Pause 将等待指定的时间,在这个例子中是一秒钟,然后转到下一个动词。
确保你的静态Web服务器仍在运行,并使用cloudflared ,运行Cloudflare隧道。
使用公开的隧道URL浏览到XML文件。[隧道-url]/call.xml
XML应该被返回到你的浏览器。 请注意这个URL。
如果你还没有Twilio账户,你可以获得一个免费的账户。(如果你在这里注册,当你升级到付费账户时,你会收到10美元的Twilio积分!)
接下来,你需要一个Twilio电话号码。去Twilio购买一个新的电话号码。电话号码的费用将被应用于你的免费促销信用。
使用左边的导航,导航到电话号码 > 管理 > 活动号码。点击你的Twilio电话号码,导航到其设置。

在 语音和传真部分找到A CALL COMES IN字段。将文本字段改为你的公共隧道URL,路径为*/call.xml*,并将HTTP动词改为HTTP GET。 
点击保存,提交表格。
为了验证呼叫是否按预期进行,用你的个人电话拨打你的Twilio电话号码。您应该听到 "Twilio通过Cloudflare Tunnel从您的本地机器收到这些指令。干得好!"。
使用一个静态文件足以证明本地隧道,但你也可以结合任何网络服务器技术使用相同的技术来处理和响应HTTP请求。
使用Cloudflared隧道在本地开发Webhooks
Webhooks是一种很好的方式,当一个事件发生时,可由外部服务通知。外部服务向你的公共网络服务发出HTTP请求,并在请求的主体中提供事件的细节。你可以使用本地隧道,通过Cloudflare Tunnels这样的隧道服务,使你的本地端口在互联网上可以访问。使用Cloudflare Tunnels,你可以将你的本地端口隧道到Cloudflare提供的一个随机公共URL。如果你使用Cloudflare DNS,你也可以免费配置一个持久的隧道URL。