Revel 不内置邮件组件,需手动集成 gomail 等库并复用 Dialer 实例;其设计遵循“最小约定”,将 SMTP 视为业务细节,由开发者控制连接复用、凭证安全与错误处理。

Revel 框架本身不内置邮件组件,没有 Mailer 类、Mail::send() 或配置驱动抽象层——它把这事交给你自己选库、自己封装、自己管控生命周期。直接结论:你得手动集成 gomail、mailgun-go 或 net/smtp,并确保连接复用、凭证安全、错误可捕获。
为什么 Revel 不提供 mailer 组件
Revel 的设计哲学是「最小约定」,只做路由、控制器、模板、session 等 Web 必需层,不打包第三方服务适配器。这和 Laravel/Symfony/Yii 明显不同:那些框架把邮件当作“标准能力”抽象出来;Revel 认为 SMTP 是业务细节,应由应用自己决定用原生 net/smtp 还是 Mailgun API,要不要加重试、是否走队列、怎么打日志——全由你控制。
常见误操作包括:
- 试图在
app/init.go里注册一个叫mailer的全局变量,然后在 controller 里调revel.AppController.Mailer.Send()—— Revel 不识别这种键名,也不会注入 - 把
gomail.NewMessage()写在 action 方法里每次新建,导致连接未复用、TLS 握手开销大、并发高时容易被 SMTP 服务器限流 - 密码写死在
conf/app.conf里,而该文件常被 git 提交,造成密钥泄露
用 gomail 实现可复用的邮件发送器
gomail(github.com/go-gomail/gomail)是最轻量也最贴合 Revel 风格的选择:无依赖、API 直白、支持附件与内嵌图片。关键不是“怎么发”,而是“怎么持有一个能复用的 gomail.Dialer 实例”。
推荐做法:
- 在
app/init.go的init()函数末尾,用revel.Config.StringDefault()读取 SMTP 配置,构造*gomail.Dialer并存入revel.BuiltinEngines或自定义全局变量(如app.MailDialer) - 配置项全部从
conf/app.conf读,但敏感字段如smtp.password必须用环境变量兜底:revel.Config.StringDefault("smtp.password", os.Getenv("SMTP_PASSWORD")) - 设置
Timeout: 10 * time.Second和MaxIdleConns: 10,避免连接耗尽;若用 Gmail/Outlook,Port填 587 +Encryption: "tls",别混用 465 和 ssl - 不要在 controller 里 new
gomail.Message后直接dialer.DialAndSend()—— 改用dialer.Send()并传入预构建的*gomail.Message,便于统一加X-Mailer头或日志 traceID
模板渲染与 HTML 邮件陷阱
Revel 的模板引擎(html/template)能直接渲染邮件正文,但要注意路径和上下文隔离:
- 邮件模板建议放在
app/views/email/下,和 Web 页面模板分开,避免混淆;调用时用revel.RenderTemplate("email/welcome.html", data),返回的是string,再喂给msg.SetBody("text/html", htmlStr) - 模板里不能访问
revel.CurrentApp或revel.Config—— 这些是 HTTP 请求上下文变量,邮件可能在异步 goroutine 里发,必须显式把所需配置(如站点域名、CDN 地址)作为data传进去 - 内联 CSS 必须手动做:Revel 模板不自动内联,外部样式表链接在邮件客户端基本失效;可用
github.com/maruel/inline-css这类工具在发送前处理 HTML 字符串 - 如果发纯文本备用版本,别只写
msg.SetBody("text/plain", plainStr)—— 要用msg.AddAlternative()才构成 multipart/alternative 结构,否则 Outlook 可能只显示空白
生产环境必须处理的三个静默失败点
Revel 发邮件最容易“以为发了,其实没发”,因为错误默认被吞掉或只打到 stdout(而生产常 redirect 到 /dev/null):
-
gomail.Dialer.DialAndSend()返回error,但 Revel controller 里没检查就 return,HTTP 接口 200 成功,邮件却卡在 DNS 超时或认证拒绝(535) - Gmail/腾讯企业邮要求发件人地址(
From:)和 SMTP 登录账号完全一致,填service@company.com却用admin@company.com登录,会静默丢弃 - 本地开发用
smtp.gmail.com测试时开了两步验证但没生成「应用专用密码」,错误是535 5.7.8 Username and Password not accepted,但gomail默认 error message 不带原始响应码,得用gomail.SetDebugWriter(os.Stderr)才能看到
真正跑通的关键,不是模板多漂亮、代码多简洁,而是让每一次发送都落到日志里、每一种失败都抛出可观测的 error、每一个 SMTP 凭证都脱离代码硬编码——Revel 把自由给你,也把责任一起给了。











