在代码里发邮件,大致有两条路:自己连 SMTP 服务器,或者调用邮件服务商的 HTTP API。这篇两种都写一遍,最后说说什么时候该选哪种。
方式一:直连 SMTP
Node.js(nodemailer)
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: "smtp.example.com",
port: 587,
auth: { user: "you@example.com", pass: process.env.SMTP_PASSWORD },
});
await transporter.sendMail({
from: '"Acme" <you@example.com>',
to: "user@example.com",
subject: "你的验证码",
text: "验证码是 382915,10 分钟内有效。",
});
Python(smtplib)
import os, smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "Acme <you@example.com>"
msg["To"] = "user@example.com"
msg["Subject"] = "你的验证码"
msg.set_content("验证码是 382915,10 分钟内有效。")
with smtplib.SMTP("smtp.example.com", 587) as s:
s.starttls()
s.login("you@example.com", os.environ["SMTP_PASSWORD"])
s.send_message(msg)
SMTP 的好处是通用,几乎所有邮箱都支持。缺点是连接慢、出错信息不直观;而且很多云服务器默认封了 25 端口,部署到线上经常发不出去。
方式二:调用邮件 API
API 走的是普通 HTTPS 请求,不受端口限制,返回的是结构化的结果,也更容易拿到投递状态。
Node.js
const res = await fetch("https://bulkmail.dev/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BULKMAIL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "Acme <hello@acme.com>",
to: "user@example.com",
subject: "你的验证码",
text: "验证码是 382915,10 分钟内有效。",
}),
});
if (!res.ok) throw new Error((await res.json()).error);
const { id } = await res.json();
Python
import os, requests
r = requests.post(
"https://bulkmail.dev/emails",
headers={"Authorization": f"Bearer {os.environ['BULKMAIL_API_KEY']}"},
json={
"from": "Acme <hello@acme.com>",
"to": "user@example.com",
"subject": "你的验证码",
"text": "验证码是 382915,10 分钟内有效。",
},
timeout=10,
)
r.raise_for_status()
print(r.json()["id"])
错误处理和重试
- 4xx(参数错误、域名没验证):重试没用,记日志、修代码。
- 429(超出额度):别立刻重试,等额度恢复或者升级套餐。
- 5xx 或网络超时:可以重试,用指数退避,比如 1 秒、2 秒、4 秒。
验证码这类邮件,建议把“发信”放到后台任务里,不要卡住用户的请求。拿到邮件 ID 后,用 Webhook 接收送达结果,比轮询省事得多。
该选哪种
个人脚本、发给自己的提醒,用 SMTP 连自己的邮箱就够了。正式产品里给用户发信,建议用 API:不受端口限制、有投递记录、退信会自动处理,出了问题也好查。