使用Go语言通过SendGrid SMTP发送邮件时,CC与BCC地址无法收到邮件
解决Go net/smtp结合SendGrid发送邮件时CC/BCC无法收到的问题
我来帮你搞定这个问题!你遇到的CC和BCC收不到邮件,但CC地址显示在收件详情里的情况,核心原因是对net/smtp.SendMail的工作机制理解有误,咱们一步步来修正:
问题根源
Go标准库的smtp.SendMail方法的第四个参数(to []string)对应的是SMTP协议中的**RCPT TO指令列表**——这才是SMTP服务器实际要投递邮件的目标地址。你现在只传入了email.To,所以CC和BCC地址根本没被纳入投递范围。而你在邮件头里添加的Cc和Bcc字段,只是用来在邮件客户端显示用的,不会触发实际的邮件投递。
另外还有个小问题:BCC的本意是密送,绝对不能出现在邮件头里,否则所有收件人都能看到密送地址,完全失去了密送的意义。
代码修正方案
1. 合并所有收件人到SendMail的目标列表
我们需要把To、CC、BCC的地址合并成一个完整的收件人列表(记得去重,避免重复发送),传给SendMail的第四个参数:
修改SendEmail函数:
func SendEmail() error { email := entity.Email{ From: "info@somewhere.cloud", To: []string{"crajuceveifre-5717@yopmail.com"}, ReplyTo: "dileulobugre-7335@yopmail.com", Subject: "Email Subject", Body: "Email Body", BCC: []string{"lecrecezufeu-9078@yopmail.com"}, CC: []string{"houcroissezitri-3721@yopmail.com"}, } emailMessageBytes := ToBytes(email) smtpAuth := smtp.PlainAuth("", "apikey", config.Config.EmailClientApiKey, config.Config.EmailClientHost) // 合并To、CC、BCC到实际投递列表,去重避免重复发送 allRecipients := make(map[string]struct{}) for _, addr := range email.To { allRecipients[addr] = struct{}{} } for _, addr := range email.CC { allRecipients[addr] = struct{}{} } for _, addr := range email.BCC { allRecipients[addr] = struct{}{} } // 转换为切片 recipientSlice := make([]string, 0, len(allRecipients)) for addr := range allRecipients { recipientSlice = append(recipientSlice, addr) } err := smtp.SendMail(config.Config.EmailClientHost+":"+config.Config.EmailClientPort, smtpAuth, email.From, recipientSlice, emailMessageBytes) if err != nil { log.Printf("smtp error: %s", err) return err } return nil }
2. 移除邮件头中的BCC字段
修改ToBytes函数,删掉添加Bcc头的代码:
func ToBytes(m entity.Email) []byte { buf := bytes.NewBuffer(nil) buf.WriteString(fmt.Sprintf("From: %s\r\n", m.From)) buf.WriteString(fmt.Sprintf("To: %s\r\n", strings.Join(m.To, ","))) buf.WriteString(fmt.Sprintf("Reply-To: %s\r\n", m.ReplyTo)) if len(m.CC) > 0 { buf.WriteString(fmt.Sprintf("Cc: %s\r\n", strings.Join(m.CC, ","))) } // 删掉下面这段Bcc头的代码,因为密送地址不能出现在邮件头里 // if len(m.BCC) > 0 { // buf.WriteString(fmt.Sprintf("Bcc: %s\r\n", strings.Join(m.BCC, ","))) // } buf.WriteString(fmt.Sprintf("Subject: %s\r\n", m.Subject)) buf.WriteString("MIME-Version: 1.0\n") writer := multipart.NewWriter(buf) boundary := writer.Boundary() buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n\n", boundary)) buf.WriteString(fmt.Sprintf("--%s\n", boundary)) buf.WriteString("Content-Type: text/html; charset=utf-8\n") buf.WriteString(m.Body) buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary)) return buf.Bytes() }
为什么这样改?
- SMTP协议规则:只有
RCPT TO指定的地址才会收到邮件,邮件头的Cc/Bcc仅用于显示,不决定投递目标。 - BCC的特性:密送地址必须隐藏,不能出现在邮件头中,否则所有收件人都能看到,违背了密送的设计意图。
- 去重处理:防止同一个地址同时出现在To/CC/BCC中时被重复发送。
这样修改后,CC和BCC地址就能正常收到邮件,同时CC地址会显示在收件详情里,BCC地址则完全隐藏,符合预期效果。
内容的提问来源于stack exchange,提问作者Md Zobayer
相关产品推荐
相关产品推荐

