应选用官方维护的 mailgun-go/v4,因其支持 context、自定义 HTTP client 和批量发送,而旧版 v1 已归档且不兼容 Go modules;初始化需传入已验证的 domain 和 api_key,From 地址必须是 verified sender,Send 后须检查 resp.StatusCode 而非仅 error。

Mailgun SDK 选哪个?官方 client 还是第三方库
Go 官方推荐且维护最活跃的是 mailgun-go(github.com/mailgun/mailgun-go),不是 Mailgun 文档里偶尔提到的旧版 gopkg.in/mailgun/mailgun.v1。后者已归档,不支持 Go modules,调用 Send 时容易 panic 或返回空错误。
安装正确版本:
go get github.com/mailgun/mailgun-go/v4
-
v4是当前稳定主干,支持 context、自定义 HTTP client、批量发送等关键能力 - 别用
go get mailgun—— 这会拉取一个未声明 module path 的 fork,版本混乱 - 如果你项目用了
replace或 vendor,确认go.mod中指向的是github.com/mailgun/mailgun-go/v4,而非v3或无版本号分支
初始化 Mailgun client 必须传 domain 和 API key
Mailgun 要求每个请求绑定具体 domain(如 mg.example.com),且该 domain 必须已在控制台验证通过。API key 分为 api_key(用于发信)和 public_api_key(仅限部分只读接口),发信必须用前者。
初始化示例:
mg := mailgun.NewMailgun("mg.example.com", "key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- domain 名必须和 Mailgun 控制台「Sending」→「Domains」里列出的完全一致(含大小写)
- API key 在「Account Settings」→「Security」→「API Keys」中获取,开头是
key-,不是pubkey- - 如果 domain 是 sandbox(沙箱模式),收件人邮箱必须提前在「Authorized Recipients」里添加,否则
Send返回401 Unauthorized
构建事务邮件时,From 地址必须是 verified sender
Mailgun 对 From 地址校验极严:要么是 domain 下 verified 的 mailbox(如 notifications@mg.example.com),要么是 sandbox domain 下的授权发件人邮箱。直接填 no-reply@example.com 会触发 550 Sender address rejected 错误。
构造 message 示例:
msg := mg.NewMessage(
"notifications@mg.example.com", // ← 必须是 verified sender
"Welcome aboard",
"Hi there!",
"recipient@domain.com",
)
msg.SetHtml("<h2>Welcome!</h2>")
- 若用 subdomain(如
auth.mg.example.com),domain 初始化和 From 地址需保持一致 - 不要依赖
SetFrom()后再覆盖 ——NewMessage(from, ...)的from参数才是实际发信地址,后续调用SetFrom()不生效 - 测试阶段建议先用 sandbox domain + 已验证邮箱,上线前切到 production domain 并完成 DNS 配置(TXT/SPF/DKIM)
Send() 调用后必须检查 error 和 response status
Send 方法返回 string(message ID)和 error,但 error 可能为 nil 即使 HTTP 状态码是 4xx/5xx —— 因为底层用 http.Client.Do,只对网络错误或 JSON 解析失败返回 error。真正的发送失败(如收件人无效、配额超限)会返回 200 + JSON body 带 "message": "Queued. Thank you." 或错误描述。
安全做法:
resp, id, err := mg.Send(ctx, msg)
if err != nil {
log.Printf("network or parse error: %v", err)
return
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Printf("mailgun API error: %d %s", resp.StatusCode, resp.Status)
return
}
- 务必检查
resp.StatusCode,尤其注意422 Unprocessable Entity(字段格式错)、401(key/domain 错)、402(余额不足) - Mailgun 的 message ID 形如
20240512162345.1234567890abcdef@mg.example.com,可用于日志追踪或查询状态,但不能当作“已送达”保证 - 高并发场景下,建议给
ctx加 timeout(如context.WithTimeout(ctx, 10*time.Second)),避免阻塞 goroutine

















