上一期我们聊了热土引擎邮件代发的优缺点——省去自建 SMTP 转发的麻烦但模板一旦创建就不能在运行时修改内容只能靠占位符做变量替换。今天我们从 Cloudflare Worker 开发者的视角把“发送一封验证码邮件”这件事从头到尾跑通。第一步在开发者面板把“料”备好在写任何代码之前你需要先在 dev.rtstu.com 完成三件事生成 mail_key。进入「邮件 → 邮件代发」页面点击生成。这个 32 位的字符串就是你的身份凭证明文只显示一次务必立刻保存。配置 SMTP 和邮箱模板。邮箱模板定义邮件的“骨架”。比如一个验证码模板可以写成h3$parm1$ 的验证/h3p您的验证码是$parm2$/p/p本邮件由 $parm3$ 发送/p$parm1$、$parm2$、$parm3$就是运行时会被替换的占位符。创建发送模板。发送模板和邮箱模板是“多对多”的关系发送时指定send_template_id即可。文档里的示例中mail_template_id 1send_template_id 1两者归属同一开发者即可通过校验。第二步Worker 代码逐段拆解下面是一个完整的 Worker 实现。我们逐段来看。环境变量与入口exportdefault{asyncfetch(request,env){// CORS 预检if(request.methodOPTIONS){returnnewResponse(null,{headers:{Access-Control-Allow-Origin:*,Access-Control-Allow-Methods:POST, OPTIONS,Access-Control-Allow-Headers:Content-Type,},});}Worker 作为前端和后端之间的中间层需要处理 CORS 预检请求。env对象里存放我们后面要用到的密钥这些通过wrangler secret put或 Dashboard 环境变量注入不写死在代码里。if(request.method!POST){returnnewResponse(Method Not Allowed,{status:405});}const{email}awaitrequest.json();if(!email){returnnewResponse(JSON.stringify({error:缺少邮箱地址}),{status:400,headers:{Content-Type:application/json},});}只接受 POST从请求体中取出收件人邮箱。这一步是验证码服务的安全入口——不能让任何人随便调用你的发信额度。生成验证码并存入 KVconstcodeString(Math.floor(100000Math.random()*900000));constexpireAtDate.now()5*60*1000;// 5 分钟有效awaitenv.VERIFY_KV.put(verify:${email},JSON.stringify({code,expireAt,attempts:0,}),{expirationTtl:300});用 KV 存验证码是最轻量的方案。expirationTtl: 300让 Cloudflare 自动清理过期记录不用自己写定时任务。attempts字段后面可以用来限制验证尝试次数防止暴力破解。调用热土引擎发送邮件这是核心部分。constmailPayload{mail_key:env.RTSTU_MAIL_KEY,mail_template_id:env.RTSTU_MAIL_TEMPLATE_ID,send_template_id:env.RTSTU_SEND_TEMPLATE_ID,to:email,parm1:用户,parm2:code,parm3:你的应用名,};parm1到parm3对应邮箱模板里的三个占位符。文档中有一条重要规则占位符仅在模板中真正出现时才会被替换传入未使用的占位符会被静默忽略。所以你可以放心地在模板里只放两个甚至一个占位符多余的参数不会导致错误。mail_key、mail_template_id、send_template_id三者必须归属于同一个开发者 ID否则接口会拒绝发送。constrespawaitfetch(https://appapi.rtstudio.top/mail/send,{method:POST,headers:{Content-Type:application/json},body:JSON.stringify(mailPayload),});constresultawaitresp.json();if(!result.success){// 邮件发送失败清理刚写入的 KV 记录awaitenv.VERIFY_KV.delete(verify:${email});returnnewResponse(JSON.stringify({error:result.message}),{status:502,headers:{Content-Type:application/json},});}热土引擎的响应格式是统一的成功时success: true失败时success: false并附带message字段说明原因比如“邮件发送 Key 无效”。这里做了一个重要处理邮件发送失败时删除 KV 中的验证码。否则用户收到一个永远收不到的验证码记录下次请求时可能被旧记录干扰。returnnewResponse(JSON.stringify({success:true}),{headers:{Content-Type:application/json,Access-Control-Allow-Origin:*,},});},};成功时只返回{ success: true }不回传验证码本身——这是基本的安全底线。几个容易踩的坑速率限制是真实存在的。热土引擎的邮件接口和所有 appapi 接口一样受 Cloudflare WAF 的 IP 级速率限制约束同一 IP 10 秒内最多 30 次请求第 31 次直接返回 429 并阻断 10 秒。在 Worker 里这意味着如果你的多个用户共享同一个出口 IP或者你的 Worker 被脚本高频调用都可能触发限速。文档里建议遇到 429 时使用指数退避不要在循环里立即重试。mail_key 不要暴露在前端。Worker 的价值就在于它是一层服务端代理RTSTU_MAIL_KEY作为 Worker 的环境变量存储前端只和 Worker 通信永远不接触真实的 mail_key。模板 ID 是整数。mail_template_id和send_template_id在开发者面板里显示为数字但在环境变量中注入时会被存为字符串。记得在 Worker 里显式转换Number(env.RTSTU_MAIL_TEMPLATE_ID)否则某些运行时可能不会自动做类型转换。验证码的验证逻辑不在这里。上面只处理了“发送”部分。实际的“校验”应该是一个独立的 Worker 路由或函数从 KV 中取出verify:${email}检查expireAt是否过期、attempts是否超限比对成功后删除记录。这部分逻辑和热土引擎无关完全由你自己的 Worker 控制。最后热土引擎的邮件代发本质上是一个“模板化的邮件发送网关”。它把 SMTP 连接、发信域名声誉、DNS 配置这些头疼的事打包好了你只需要关心三件事模板 ID、收件人、占位符的值。对于在 Cloudflare Worker 上做验证码发送这个场景来说这个抽象层次是合适的——不多不少。下一期可以聊聊验证码的校验逻辑怎么写才安全以及如何用 Worker 的scheduled事件做发送频率的二次限制。
企业数字化 ERP 产品动态
相关推荐
FreeRTOS Queue 深度解析:看懂“数据中转站”背后的内存、阻塞与调度机制 在 FreeRTOS 中,Queue(队列)是最常用的任务间通信机制之一。
很多工程师知道它可以用来“传数据”,也会使用 xQueueSend()、xQueueReceive(),但如果进一步追问:
队列到底存在哪里?发送数据时究竟发生了什么?为什么发送方修改原变量后,队列里的数据不会变化?队列满了… · 2026/9/26 19:23:03
5000个Agent落地造车一线:企业级Agent规模化开发与运维实战 1. 从"造车"到"造智能体":5000个Agent落地背后的真实命题第一次看到"5000个智能体落地造车一线"这个数字,我的反应不是震撼,而是怀疑。做过企业级Agent项目的人都知道,Demo跑通和规模化落地之间隔着… · 2026/9/26 19:22:57
数据库实验五实战:从SQL增删改查到索引与连接池 简介:面向西北工业大学软件学院数据库课程的实验五配套资源,是一份完整的电子商务数据库概念模型设计示例。压缩包共二十个文件、体积仅二百八十二KB,文件类型以gif动图、doc与txt文档、cdm模型文件和htm网页为主:gif演示了从注册… · 2026/9/26 19:22:57
LibreChat实战:开源自托管AI对话网关,统一管理多模型API 先聊点实在的:如果你跟我一样,电脑上开着五六个标签页,轮着在ChatGPT、Claude、Gemini这些官方网页之间来回切,问一个问题还要手动把历史记录搬来搬去,那LibreChat这个项目你一定会看上眼。LibreChat是一个开源、可自托… · 2026/9/26 20:02:30
MCP传输方式详解:stdio与SSE架构对比及选型指南(TaoToken配置实战) /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 20:02:30
小程序数据统计工具怎么挑?评分对比与选型维度 2026年9月22日|CSDN 技术社区直答:挑小程序数据统计工具,先别盯着“免不免费”,而是按接入便捷性、分析深度、渠道归因、价格、多端、性能六个维度打分,再结合自己最想回答的三个问题来缩小候选。很多微信小程序团队上… · 2026/9/26 20:02:11
C语言编译链接 1.翻译环境是指源代码->可以执行文件,程序还没有运行。其又分为编译和链接。编译又分为预处理(预编译),编译,汇编2.预处理:处理所有#开头指令并输出.i注意:宏替换发生在预处理阶段3. 编译&am… · 2026/9/26 20:02:11
假期值守无人直播,我在告警日志里记下六条碎片 中秋三天假期,替朋友盯了两晚无人直播的值守。屏幕里的直播一帧没跳,倒是中控台的告警日志让我记了不少东西。挑六条出来,都是碎的,但拼起来就是假期值守的全貌。
碎片一:告警去重比告警本身重要。第一晚十一点到十二点… · 2026/9/26 20:02:11
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46