本博客最初由Twilio技术内容首席软件工程师Miguel Grinberg发表。
大多数网络应用在注册过程中接受用户的电子邮件地址。为了防止创建欺诈性账户,确保用户能够通过给定的电子邮件地址接收电子邮件始终是一个好主意。
Twilio Verify是一项服务,允许你通过短信、语音电话或电子邮件向用户发送一个数字代码来验证用户。本教程将告诉你如何在你的FastAPI应用程序中使用电子邮件实现一个验证流程。
教程要求
- Python 3.6或更高版本 - 如果你的操作系统没有Python解释器,请从python.org下载安装程序。
- Twilio账户 - 如果你是Twilio的新用户,请点击这里创建一个免费账户。如果你使用这个链接创建一个账户,你将收到10美元的信用额度,在你升级到付费账户时使用。在此查看试用账户的限制
- SendGrid账户。点击这里,创建一个免费账户。使用这个账户,你每天最多可以发送100封电子邮件
配置SendGrid
为了设置电子邮件验证方案,你需要将你的Twilio账户与你的SendGrid账户连接。在这一节中,我们将在SendGrid方面进行必要的准备工作。
创建一个动态模板
第一步是创建一个电子邮件模板,Twilio Verify服务将使用该模板向用户发送验证码。
在SendGrid仪表板上,点击左侧菜单中的电子邮件API,然后点击动态模板。
[点击'创建动态模板'按钮,创建一个新的模板。给这个新模板一个你选择的名字。在本教程中,我们将使用**"email-verification**"这个名字。
[点击创建按钮后,你会被带到动态模板页面,在那里你会看到新创建的 "电子邮件验证 "模板。点击模板名称来展开它,并看到下面截图中显示的细节。
[一旦你点击了 "添加版本 "按钮并创建了你的第一个版本的模板,你将会看到一个预先准备好的模板的选择。在本教程中,为了方便,我们将选择空白模板。然而,如果你熟悉HTML,你可以从这个模板列表中选择。
下一个提示将要求你为模板选择一个编辑器。[选择 "代码编辑器",这将让你直接访问电子邮件正文中的HTML代码。
空白模板实际上并不是空白的,因为它有一个页脚,上面有一个取消订阅的链接。我们将保持这个链接不变,并添加以下HTML行,以允许将文本插入到电子邮件的正文中。添加它的地方是在电子邮件正文的上方,就在<body>元素标签之后。
<p>{{twilio_message}}.</p>
下图在代码编辑器中显示了这段话将如何融入电子邮件中。
在这个模板中,{{twilio_message}}是一个占位符,它将被Twilio替换成电子邮件的文本。该电子邮件将包含类似 "认证代码是XXXXXX "的内容。
如果{{twilio_message}}不适合你的需要,有几个占位符可以用来设计电子邮件的正文。欲了解更多信息,请参考本文件。
如果你想看看你的电子邮件中的实际数据会是什么样子,点击页面顶部的测试数据标签,并输入JSON格式的twilio_message变量的样本值。例如,你可以输入以下内容
{"twilio_message": "Your verification code is XXX"}
如果你对你所创建的模板感到满意,点击页面左上方的设置按钮,并输入你的邮件主题。
点击导航栏中的 "保存 "按钮,并按页面左上角的左箭头按钮,返回 "动态模板 "页面。
新的电子邮件模板将被分配一个 "模板ID"。你以后在设置Twilio账户时需要这个ID,请看下面的截图,看看ID出现的位置。
创建一个API密钥
设置SendGrid的第二步是创建一个API密钥,Twilio可以用它来向你的用户发送认证邮件。
从仪表板上,选择设置,然后选择API密钥。[在API密钥页面,点击 "创建API密钥 "按钮。
给你的API密钥起个名字。同样,你可以给它起任何你喜欢的名字,但我们使用**"电子邮件验证**"这个名字。从名称下面的三个选项中,选择**"完全访问**"。
[点击 "创建和查看 "按钮,创建你的密钥,你的API密钥将显示在下一页。这是你唯一能够看到你所创建的钥匙的时候。复制钥匙并将其保存为文本。它将随时可用,直到你在下一节中需要它。
设置Twilio
Twilio验证服务使用SendGrid API密钥和你在上一节中设置的动态模板来发送电子邮件。现在我们将去你的Twilio账户完成设置,并将你的Twilio账户与你的SendGrid账户联系起来。
创建一个电子邮件链接
在Twilio控制台,点击【所有产品和服务】按钮,你会看到【验证】菜单。点击下面的"电子邮件集成"。
[点击 "创建电子邮件集成 "按钮,创建一个新的电子邮件集成。如果一个电子邮件集成已经存在,请点击[+]号来添加另一个。
你会被提示给电子邮件验证一个名称。这里我们将其命名为**"电子邮件验证**"。
一旦你给了它一个名字,你就可以输入你的SendGrid账户的详细信息。你将需要指定以下信息
- SendGrid API密钥(在上一节中创建)。
- 动态模板的模板ID(在上一节创建)。
- 在认证电子邮件的[发件人]栏中使用的电子邮件地址
- 在认证邮件的[发件人]栏中使用的名称。在这一领域输入你的网站名称或公司名称。
一旦你填写了上面的字段,点击 "保存 "来保存这个电子邮件集成。
创建一个验证服务
[从'验证'菜单中选择'服务'。然后点击立即创建服务按钮。如果你的账户已经有一个验证服务,请点击[+]按钮添加一个新的服务。
给服务一个描述性的名字。你在这里给出的名字将出现在发给用户的验证邮件中。因此,建议你输入你的网站或公司的名称。在本教程中,我们将我们的公司命名为**"我的公司**"。
接下来,我们将介绍在这个服务中可以编辑的配置屏幕。在页面的顶部,你会看到一个下拉菜单,你可以设置验证码中使用的数字数量。这里我们选择了8位数字,如下图所示。
向下滚动到电子邮件集成部分,在下拉菜单中选择你刚刚创建的电子邮件集成。
向下滚动到底部,你会看到交付渠道部分。在这个例子中,我们将只使用电子邮件,所以最好是禁用其他两个渠道。
点击[保存]按钮,保存您的更改。现在你已经配置了你的电子邮件认证服务,并准备开始编码你的FastAPI应用程序。
设置你的项目
在本节中,你将设置你的新FastAPI项目。为确保这一过程有序进行,打开终端或命令提示符,找到一个合适的位置来创建一个新的目录。这是你将运行你将要创建的项目的地方。
mkdir fastapi-verify-email
cd fastapi-verify-email
创建一个虚拟环境
按照Python的最佳实践,我们现在将创建一个虚拟环境。在虚拟环境中,我们将安装本项目所需的Python依赖项。
如果你使用的是Unix或macOS系统,打开终端并输入以下命令
python3 -m venv venv
source venv/bin/activate
如果你在Windows上运行教程,在命令提示符窗口中输入以下命令
python -m venv venv
venv\Scripts\activate
一旦你启用了虚拟环境,你就可以安装这个项目所需的Python依赖项。
pip install fastapi python-dotenv aiofiles python-multipart uvicorn twilio
在这个项目中,我们将使用的六个Python包是
- FastAPI框架 - 用于创建网络应用程序
- python-dotenv - 从_.env_文件导入应用程序配置
- aiofiles - 用FastAPI服务静态文件
- python-multipart - 提供用FastAPI处理表单数据的能力。
- uvicorn - 提供一个FastAPI应用程序
- 用于处理Twilio API的TwilioPython助手库
定义应用程序的配置
为了使用Twilio Verify发送验证邮件,FastAPI应用程序需要访问并验证Twilio账户的凭证。你还需要用你刚刚创建的Twilio Verify服务ID来配置这个应用程序。
定义这些配置值的最安全方式是为它们设置环境变量,而在FastAPI应用程序中管理环境变量的最方便方式是使用_.env_文件。
在文本编辑器中打开一个新文件,将其命名为".env"(前面有一个点),并输入以下内容
TWILIO_ACCOUNT_SID=xxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxxx
TWILIO_VERIFY_SERVICE=xxxxxxxxx
所有xxxxxxxxx,都应该用相应的正确数值来代替。前两个变量是Twilio的[Account SID]和[Auth Token]。这些值可以在Twilio控制台仪表板上找到(见下文)。
TWILIO_VERIFY_SERVICE变量是分配给该服务的[SERVICE SID]的值。这个值可以在验证服务配置页面找到。
要将上述三个变量导入你的FastAPI应用程序,创建一个名为_config.py_的文件并输入以下信息。
from pydantic import BaseSettings
class Settings(BaseSettings):
twilio_account_sid: str
twilio_auth_token: str
twilio_verify_service: str
class Config:
env_file = '.env'
FastAPI使用pydanticBaseSettings类来管理配置。 BaseSettings 的子类会自动从环境变量中导入定义为属性的变量。另外,它们也可以通过使用dotenv直接从_.env_文件中导入。
下一节将介绍如何使用Settings类。
使用FastAPI的电子邮件认证
这是你将为你的FastAPI应用程序编写代码的地方。
基本的FastAPI应用程序
下图显示了FastAPI应用程序的第一次迭代。在这个版本中,只返回主页面。主页面包含一个网络表格,你可以输入并验证你的电子邮件地址。
在文本编辑器或IDE中打开一个新文件,将其命名为_app.py_,并输入以下代码
import asyncio
from fastapi import FastAPI, Form, Cookie, status
from fastapi.responses import FileResponse, RedirectResponse
from twilio.rest import Client
import config
settings = config.Settings()
app = FastAPI()
client = Client(settings.twilio_account_sid, settings.twilio_auth_token)
@app.get('/')
async def index():
return FileResponse('index.html')
settings变量导入你在上一节中保存在_.env_文件中的配置变量。app变量代表FastAPI应用程序。 client 变量是Twilio客户端库的一个实例,用于向Twilio API发送请求;Twilio客户端是用从配置中加载的凭证初始化的。发送请求时,需要凭证进行认证。
@app.get(‘/’)装饰器定义了一个端点,它被映射到应用程序的根URL。当端点被实现时,会返回一个响应,该响应是从一个名为_index.html_的静态文件加载的。
为了使这个端点工作,你需要创建一个HTML文件。在你的编辑器或IDE中打开一个新文件,将其命名为_index.html_,并输入以下HTML代码
<!doctype html>
<html>
<head>
<title>FastAPI Email Verification Example</title>
</head>
<body>
<h1>FastAPI Email Verification Example</h1>
<form method="post">
<label for="email">Your email:</label>
<input name="email" id="email">
<input type="submit" value="Submit">
</form>
</body>
</html>
这个HTML页面将创建一个有单一字段的表单,提示输入电子邮件地址。
运行服务器
该应用程序尚未完成,但它已完全具备测试功能。确保你有到目前为止在项目目录下创建的_app.py_、index.html_和.env_文件,然后用以下命令启动应用程序
uvicorn app:app --reload
Uvicorn是运行FastAPI应用程序的推荐服务器。 有了--reload,uvicorn将监控源文件,并在每次文件更改后自动重启服务器。在你参考本教程的其余部分时,让服务器运行是安全的。
要检查应用程序是否正在运行,请打开一个网络浏览器,在地址栏中进入_http://localhost:8000と入力します_。浏览器将加载应用程序的主页面,它看起来应该是这样的
处理表格数据
当你试图提交表单时,FastAPI将返回一个错误信息 "方法不允许"。这是因为表单提交还没有实现。
在_index.html_的<form>元素中,表格是由method属性(设置为post)和没有action的属性。这意味着该表格将通过POST请求被发送到原始的URL。在这个例子中,它是应用程序的根URL。
处理提交表格的端点有以下结构你可以把这个添加到_app.py_的底部,但要记住以# TODO开始的那一行。这一行显示了尚未建立的部分功能。
@app.post('/')
async def handle_form(email: str = Form(...)):
# TODO: send the verification email
# TODO: return a response to the client
这第二个端点使用@app.post(‘/’)装饰器来定义POST请求的处理程序。这个web表单有一个字段,email,它是传递给函数的参数。fastAPI解析表单数据,提取客户传递的字段的值,并将其发送给带有这个参数的函数功能。
我们将在后面的章节中完成这个端点。
发送验证码
当handle_form()函数被调用时,应用程序将收到一个需要验证的电子邮件地址,并使用先前创建的Twilioclient实例向用户发送一封电子邮件。你可以这样做。然而,有一个小问题。
用于Python的Twilio Helper库不支持异步应用。这个库将创建一个网络请求到Twilio服务器,所以如果你在异步函数中直接使用它,它将阻塞asyncio的循环。为了避免阻塞循环,所有与Twilio相关的工作都可以封装在执行器中运行的函数中。这使应用程序能够顺利运行。
下面的代码是_app.py_中handle_form()函数的更新版本,使用的逻辑是在执行器中执行send_verification_code()函数。
@app.post('/')
async def handle_form(email: str = Form(...)):
await asyncio.get_event_loop().run_in_executor(
None, send_verification_code, email)
# TODO: return a response to the client
使用asyncio循环的run_in_executor()方法可以使阻塞函数在另一个线程或进程中执行,因此循环不会被阻塞。第一个参数是你想使用的EXECUTOR。另外,如果你能顺利使用默认的线程执行器,可以None。其余的参数是要执行的函数和它的位置参数。
让我们来看看send_verification_code()函数的实现。注意,这是标准的同步代码,所以这个函数没有用async关键字定义。我们将在_app.py_中加入这个函数。
def send_verification_code(email):
verification = client.verify.services(
settings.twilio_verify_service).verifications.create(
to=email, channel='email')
assert verification.status == 'pending'
这个函数使用Twilio客户端的一个实例。这个实例是以全局范围创建的,并创建一个认证资源。新创建的认证资源将有一个 "待定 "的状态。然后,该函数使用assert语句来确保该对象处于预期状态。
在这个阶段,Twilio将使用SendGrid的API密钥和模板向指定地址发送电子邮件。该电子邮件将包含一个随机生成的数字代码。
发送回复
在上一节中,handle_form()的功能没有完成:在执行者发送了认证代码后,服务器需要向客户的网络浏览器返回一个响应。
处理表单提交的最常见的过程是一个带有重定向的响应。这就避免了一些潜在的问题,例如,重复提交表格,或浏览器向用户发出混乱的警告。这被称为PRG模式(Post/Redirect/Get的缩写)。
在这个应用中,这就是浏览器需要显示第二个表单的地方。这个表格是用用户在电子邮件中收到的验证码填充的;第二个表格是在_/verify_端点下实现的。因此,这里必须发生重定向。 一个完整的handle_form()功能的实现是这样的:更新你的_app.py_的版本。
@app.post('/')
async def handle_form(email: str = Form(...)):
await asyncio.get_event_loop().run_in_executor(
None, send_verification_code, email)
response = RedirectResponse('/verify',
status_code=status.HTTP_303_SEE_OTHER)
response.set_cookie('email', email)
return response
响应指示浏览器立即重定向到_/verify_URL。这个应用程序的一个有趣的方面是,在验证代码时,需要再提供一次用户提供的电子邮件地址。为了确保地址不会丢失,该函数添加了一个会话cookie,并在返回响应之前存储了地址。
代码验收
用户收到了一封带有数字代码的电子邮件。现在,浏览器需要显示第二个网络表单。在这个表格中,输入代码并验证电子邮件地址。
对_/verify_端点的GET请求返回一个带有表单的HTML页面。我们将把这个端点添加到_app.py_中。
@app.get('/verify')
async def verify():
return FileResponse('verify.html')
这个端点指的是_verify.html_文件。在你的项目目录下创建这个参考文件,并在其中填入以下信息
<!doctype html>
<html>
<head>
<title>FastAPI Email Verification Example</title>
</head>
<body>
<h1>FastAPI Email Verification Example</h1>
<form method="post">
<label for="code">Verification code:</label>
<input name="code" id="code">
<input type="submit" value="Submit">
</form>
</body>
</html>
验证代码
验证代码的逻辑需要再次调用Twilio API。因此,我们需要另一个补充同步函数在执行器中运行。在_app.py_中添加以下函数
def check_verification_code(email, code):
verification = client.verify.services(
settings.twilio_verify_service).verification_checks.create(
to=email, code=code)
return verification.status == 'approved'
check_verification_code()功能需要一个电子邮件地址和一个数字代码,并将这些发送到Twilio验证服务进行验证。如果验证成功,返回的验证检查资源的状态将是approved。如果代码不正确,返回资源的状态将是pending。该函数检查状态,如果输入的代码正确,则返回True,如果不正确则返回False。
当用户提交代码进行验证时,浏览器会向_/verify_端点发出一个POST请求。下图显示了该请求可用的处理程序。我们在_app.py_中加入这个函数。
@app.post('/verify')
async def verify_code(email: str = Cookie(None), code: str = Form(...)):
verified = await asyncio.get_event_loop().run_in_executor(
None, check_verification_code, email, code)
if verified:
return RedirectResponse('/success',
status_code=status.HTTP_303_SEE_OTHER)
else:
return RedirectResponse('/verify',
status_code=status.HTTP_303_SEE_OTHER)
这个端点接受两个参数。 email 参数取自我们刚刚设置的cookie。另一方面,code的论点,是发出的。这些参数被传递给check_verification_code()函数。这个函数需要在执行器中执行,这样它就不会阻塞异步循环。
如果代码被成功验证,这个端点的响应将被重定向到_/success_ URL。如果代码不能被验证,将产生一个重定向到_/verify_端点。这是显示接受代码的表单的端点,用户可以在这个表单中输入新代码。
下图显示了_/success_端点的实现。这个处理程序在_app.py_的最后运行。
@app.get('/success')
async def success():
return FileResponse('success.html')
这是一个简短的处理程序,显示一个HTML页面;创建文件_success.html_,并复制以下内容
<!doctype html>
<html>
<head>
<title>FastAPI Email Verification Example</title>
</head>
<body>
<h1>FastAPI Email Verification Example</h1>
<p>You have been verified!</p>
<p><a href="/">Verify another email?</a></p>
</body>
</html>
这个页面通知用户,认证成功了。该通知包含一个通往主页的链接,以备你验证其他电子邮件地址。
测试应用程序
现在是你一直在等待的时刻。确保_app.py_已经更新,并且我们提到的所有函数都存在:config.py,.env,index.html,verify.html_和_success.html。文件在你的项目目录中。
如果你从教程开始就一直在运行uvicorn,你可以照样继续,因为每次对源文件进行修改时,服务器应该都会自动重新启动。如果服务器目前没有运行,你可以用以下命令启动它
uvicorn app:app --reload
打开一个网络浏览器,进入_http://localhost:8000に移動してください_。在表格中输入你的电子邮件地址。点击提交按钮,一封包含验证码的电子邮件将立即发送给你。一旦你输入了代码,你的电子邮件地址就会被验证。
总结
关于如何使用FastAPI和Twilio Verify验证电子邮件地址的解释到此结束。本文学到的技术也可以应用于其他框架。特别适合的是Quart、Sanic和Tornado,它们也是基于asyncio的。关于如何在你的异步应用程序中使用Twilio的更多信息,请参阅博文《在你的异步应用程序中使用Twilio Python辅助库》。关于如何使用Twilio for FastAPI的更多提示,请阅读这篇关于如何发送短信的文章。
我们迫不及待地想看到你使用Twilio和FastAPI建立的东西。
Miguel Grinberg是Twilio公司负责技术内容的首席软件工程师。这个博客是一个展示你的项目的好地方。请通过mgrinberg [at] twilio [dot] com联系他。