常见原因是现代邮箱禁用密码登录,需用应用专用密码或授权码;MIME头部缺失或编码错误导致乱码;gomail需手动加context超时和重试逻辑。

用 net/smtp 发送纯文本邮件时,认证失败的常见原因
Go 标准库 net/smtp 不自带 OAuth2 支持,直接填邮箱密码大概率失败——现代邮箱(如 Gmail、QQ 邮箱)已禁用“密码登录”,必须用“应用专用密码”或开启 SMTP 服务权限。
实操建议:
- Gmail:需在 Google 账户中开启“两步验证”,再生成“应用专用密码”,用该 16 位密码替代账户密码
- QQ 邮箱:进入“设置 → 账户 → POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务”,开启 SMTP 服务,获取授权码(非登录密码)
- 连接地址和端口要匹配:Gmail 用
smtp.gmail.com:587(STARTTLS),QQ 邮箱用smtp.qq.com:587;若用:465则需改用smtp.Dial+ TLS 显式握手,不能直接传auth
发送带附件或 HTML 内容时,为什么收件方看到乱码或空白?
根本原因是 MIME 头部缺失或编码错误。Go 的 net/smtp 不提供高级封装,需手动构造符合 RFC 2045/2047 的多部分邮件(multipart/mixed 或 multipart/alternative)。
关键点:
- HTML 正文必须声明
Content-Type: text/html; charset=utf-8,且内容本身用 UTF-8 编码(避免用gob或json.Marshal直接塞字符串导致转义) - 附件文件名含中文时,必须用
encoded-word编码(如=?UTF-8?B?5byg5LiJLmpwZw==?=),否则 Outlook 等客户端可能显示为noname - 推荐用
gomail库替代手写 MIME:它自动处理边界符、编码、换行符(CRLF),调用m.SetHeader("From", "a@example.com")比拼接字符串安全得多
使用 gomail 发送时,如何控制超时与重试逻辑?
gomail 默认无超时,网络抖动或 SMTP 服务器响应慢会导致协程卡死。它也不内置重试,需自行包装。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
实操建议:
- 用
context.WithTimeout包裹dialer.Dial和send.Send调用,例如 10 秒超时:ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() err := dialer.DialAndSendWithContext(ctx, msg)
- 简单重试可用
for i := 0; i +time.Sleep(2 * time.Second),但注意:SMTP 连接失败可重试,而 5xx 响应(如554 Transaction failed)说明内容被拒,重试无意义 - 生产环境建议把发送逻辑抽成异步任务(如发到 Redis 队列),失败后记录错误码(
err.Error()中含535 5.7.8等标准码),便于分类告警
本地开发调试时,如何不依赖真实 SMTP 服务?
硬连 Gmail/QQ 既慢又易触发风控,且无法验证收件格式是否正常。最佳做法是启动一个轻量 SMTP 接收器,只打印原始邮件内容。
推荐方案:
- 用
go-smtp库起一个本地监听服务(localhost:1025),所有发信指向它,日志输出完整 MIME 结构,确认附件、HTML、编码是否正确 - Docker 用户可直接运行
mailhog/mailhog:docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog
,然后 Go 代码中设smtpAddr = "localhost:1025",打开http://localhost:8025查看可视化邮件 - 注意:MailHog 不校验认证,所以
gomail.NewDialer("localhost", 1025, "", "")第三、四参数可为空,避免本地调试时还要配假密码
实际部署时最容易被忽略的是:不同 SMTP 服务商对单日发信量、每分钟连接数、发信频率有隐性限制,且错误响应码含义差异大——比如腾讯企业邮返回 550 User has no permission 可能是域名未完成 SPF/DKIM 配置,而不是账号问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










