确认邮件

在上一章中,我们介绍了新订阅者电子邮件地址的验证——它们 必须符合电子邮件格式。

现在,我们拥有的电子邮件地址在语法上是有效的,但我们仍然不确定它们是否存在:

真的有人使用这些电子邮件地址吗?他们能联系到吗?

我们对此一无所知,只有一种方法可以找到答案:发送一封真正的确认电子邮件。

订阅者同意

你的蜘蛛感应现在应该开始运转了——在订阅用户生命周期的这个阶段,我们真的需要知道吗?难道我们不能等到下一期简报发布才能知道他们是否收到了我们的邮件吗?

如果我们唯一关心的是进行彻底的验证,我同意:我们应该等到下一期发布,而不是给我们的 POST /订阅端点增加更多复杂性。

不过,还有一件事我们关心,而且我们无法推迟:订阅者的同意。

电子邮件地址不是密码——如果你上网时间足够长,你的电子邮件地址很可能并不难获得。

某些类型的电子邮件地址(例如专业电子邮件地址)是完全公开的。

这为滥用提供了可能性。

恶意用户可能会开始订阅互联网上各种简报的电子邮件地址,从而让受害者的收件箱充斥着垃圾邮件。

相反,不正当的新闻通讯所有者可能会开始从网络上抓取电子邮件地址,并将其添加到其新闻通讯电子邮件列表中。

这就是为什么 POST /subscriptions 请求不足以表示“此人想接收我的新闻通讯内容!”。

例如,如果您与欧洲公民打交道,则法律要求获得用户的明确同意。

这就是为什么发送确认电子邮件已成为一种常见做法:在新闻通讯 HTML 表单中输入您的详细信息后,您将在收件箱中收到一封电子邮件,要求您确认是否确实想要订阅该新闻通讯。

这对我们来说非常有效——我们保护用户免受滥用,并在尝试向他们发送新闻通讯之前确认他们提供的电子邮件地址确实存在。

确认用户流程

让我们从用户的角度来看一下我们的确认流程。 他们会收到一封包含确认链接的电子邮件。 点击链接后,系统会自动将用户重定向到成功页面(“您现在订阅了我们的新闻通讯!太棒了!”)。从此以后,他们会在收件箱中收到所有新闻通讯。 后端是如何工作的? 我们会尽量简化流程——我们的版本不会在确认后执行重定向, 我们只会向浏览器返回 200 OK 状态码。 每次用户想要订阅我们的新闻通讯时,他们都会触发一个 POST /subscriptions 请求。 我们的请求处理程序将:

  • 将用户的详细信息添加到我们数据库的 subscriptions 表中,状态为 pending_confirmation
  • 生成一个(唯一的)subscription_token
  • subscription_token 存储在我们数据库的 subscription_tokens 表中,并根据用户的 ID 进行存储;
  • 向新订阅者发送一封电子邮件,其中包含结构为 https://<our-api-domain>/subscriptions/confirm 的链接
  • 返回 200 OK。

一旦用户点击链接,浏览器标签页就会打开,并向我们的 GET /subscriptions/confirm 端点发送 GET 请求。我们的请求处理程序将:

  • 从查询参数中检索 subscription_token;
  • 从 subscription_tokens 表中检索与 subscription_token 关联的订阅者 ID;
  • 在 subscriptions 表中将订阅者状态从 pending_confirmation 更新为 active;
  • 返回 200 OK。

还有其他一些可能的设计(例如,使用 JWT 代替唯一令牌), 并且我们还有一些特殊情况需要处理(例如,如果他们点击链接两次会发生什么?如果他们尝试订阅两次会发生什么?)——随着实施工作的进展,我们将在最合适的时间讨论这些问题。

实施策略

这里有很多工作要做,所以我们将工作分为三个概念块:

  • 编写一个模块来发送电子邮件
  • 调整现有 POST /subscriptions 请求处理程序的逻辑以匹配新规范
  • 从头编写 GET /subscriptions/confirm 请求处理程序

让我们开始吧!