smtp配置失败主因是服务端策略与客户端协议未对齐:①端口587需先tls.dial再传smtp.client,不可用smtp.dial直连;②端口465必须tls.dial隐式tls直连;③gmail需两步验证+应用专用密码;④qq邮箱授权码仅对smtp.qq.com:587或ssl.smtp.qq.com:465有效;⑤企业邮箱需后台开启smtp并用专用域名。

net/smtp 本身不是框架,它只是 Go 标准库里的一个协议传输层,不处理模板、附件、重试或可观测性——直接用它在生产环境发邮件,大概率会卡在认证失败、乱码、进垃圾箱或超时阻塞上。
SMTP 配置为什么总连不上?
根本不是代码写错了,而是服务端策略和客户端协议没对齐:
-
smtp.Dial不自动升级 STARTTLS,端口587必须先tls.Dial再传给smtp.Client;端口465是隐式 TLS,必须用tls.Dial直连,不能用普通net.Dial - Gmail 用户必须开两步验证,再生成 16 位「应用专用密码」——填错一个字母或带空格就认证被拒
- QQ 邮箱的 SMTP 地址是
smtp.qq.com,但启用后拿到的授权码只对smtp.qq.com:587或ssl.smtp.qq.com:465有效,混用会静默失败 - 企业邮箱(如腾讯企业邮)默认关闭 SMTP,得去管理后台手动开启,并确认域名用的是
smtp.exmail.qq.com而非公共版smtp.qq.com
邮件正文为什么显示乱码或源码?
net/smtp.SendMail 只管发字节流,MIME 结构全靠手拼——少一个 \r\n、错一个 Content-Type 头、中文没 base64 编码,收件方就解析失败:
- 纯文本邮件必须带头:
Content-Type: text/plain; charset=utf-8+Content-Transfer-Encoding: base64,正文用base64.StdEncoding.EncodeToString([]byte(body))编码 - HTML 邮件同理,
Content-Type改为text/html,且所有"和得转成 <code>"和,否则 Outlook 直接当纯文本渲染 - 带附件别自己拼
boundary——用mime/multipart包:w := multipart.NewWriter(wr),再调w.CreatePart()和w.CreateFormFile(),它自动处理换行和分隔符 - Subject 中文必须 base64 编码并加头:
Subject: =?UTF-8?B?...?=,不能直接写字符串
怎么避免发信被当成垃圾邮件?
SMTP 协议不校验发件人,但接收方(QQ、163、Gmail)会查 DNS 记录和行为特征:
- From 地址必须和认证账号完全一致,比如认证用
notify@company.com,就不能设 From 为no-reply@other.com - 国内云服务器(阿里云、腾讯云)25 端口默认封禁,必须用
465或587;本地开发别直连公网 SMTP,用mailhog搭测试端:docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog - 单 IP 每分钟发信数超限(Gmail 通常 ≤100 封/天)、HELO 域名不存在、无 SPF 记录,都会触发限流或静默丢弃
- 生产环境别直连 Gmail/QQ,改用 Mailgun、SendGrid 或 Amazon SES——它们提供 API、退信分析、送达率监控,且已预配好 DKIM/DMARC
要不要封装成“通知框架”?
硬写 SendEmail()、SendSMS() 函数只会让业务逻辑和渠道强耦合。真要扩展成多通道(邮件 / 站内信 / 短信),必须先定义接口:
- 定义
Notifier接口:Notify(ctx context.Context, msg *Message) error,其中Message至少含ID、UserID、Title、Content、Channel(建议用常量ChannelEmail而非字符串字面量) - 模板渲染必须前置,
Notify()只接收已渲染好的 HTML 或文本,不处理变量注入 - 每个实现(如
SMTPNotifier)要自带超时控制:ctx, cancel := context.WithTimeout(ctx, 30*time.Second),防止卡死阻塞整个任务流 - Channel 字段别用字符串散落各处,统一定义常量,后续加新渠道(比如钉钉机器人)才不会漏改
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











