使用FakeSMTP创建本地SMTP服务器接收邮件具体实现。

以下代码来自Let's Go further节选。具体说明均为作者本人理解。

编辑邮件模版

主要包含三个template:

  1. subject:主题
  2. plainBody: 纯文本正文
  3. htmlBody:超文本语言正文
html 复制代码
{{define "subject"}}Welcome to Greenlight!{{end}}
{{define "plainBody"}}
Hi,
Thanks for signing up for a Greenlight account. We're excited to have you on board!
For future reference, your user ID number is {{.ID}}.
Thanks,
The Greenlight Team
{{end}}
{{define "htmlBody"}}
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>Hi,</p>
<p>Thanks for signing up for a Greenlight account. We're excited to have you on board!</p>
<p>For future reference, your user ID number is {{.ID}}.</p>
<p>Thanks,</p>
<p>The Greenlight Team</p>
</body>
</html>
{{end}}

创建mailer.go

主要功能为解析,执行邮件模版,将邮件正文加载到bytes.Buffer中,最后打包信息发送

go 复制代码
package mailer

import (
	"bytes"
	"embed"
	"github.com/go-mail/mail/v2"
	"html/template"
	"time"
)

// 声明一个embed.FS变量存储email模板,在他上面有一个注释指令表明我们想要存储哪个文件的模板
//
/*
	1. 只能在包级别的变量上使用//go:embed指令,不能在函数或者方法中使用
	2. 路径应该相对于该指令的源代码
	3. 路径不能包含.or..,也不能以/开头或结尾,这实际上限制了你只能嵌入与源代码位于同一目录(或子目录)的文件
	4. 如果路径指向一个目录,那么会递归加载该目录下的所有文件,但是不会加载.  /开头的文件,如果需要加载,需要在路径中使用通配符//go:embed "templates/*"
	5. 可以在一个指令中指定多个目录和文件
*/

//go:embed "templates"
var templateFS embed.FS

// Mailer mail.Dialer用于连接SMTP服务器  email的发送信息,包含名字和地址"Alice Smith <alice@example.com>"
type Mailer struct {
	dialer *mail.Dialer
	sender string
}

func New(host string, port int, username, password, sender string) Mailer {
	//使用SMTP服务器的配置初始化一个mail.Dialer实例,发送邮件时使用五秒的超时配置
	dialer := mail.NewDialer(host, port, username, password)
	dialer.Timeout = 5 * time.Second

	return Mailer{
		dialer: dialer,
		sender: sender,
	}
}

// Send
//
//	@Description:
//	@receiver m
//	@param recipient  接受者邮件地址
//	@param templateFile 模版文件名
//	@param data 模板的动态数据
//	@return error
func (m *Mailer) Send(recipient, templateFile string, data interface{}) error {
	//1. 解析模板 从embed文件系统解析请求模板
	parseFS, err := template.New("email").ParseFS(templateFS, "templates/"+templateFile)
	if err != nil {
		return err
	}
	//2. 执行模版 执行模板文件,将参数传递进去,将结果存储在bytes.Buffer变量中
	subject := new(bytes.Buffer)
	err = parseFS.ExecuteTemplate(subject, "subject", data)
	if err != nil {
		return err
	}
	plainBody := new(bytes.Buffer)
	err = parseFS.ExecuteTemplate(plainBody, "plainBody", data)
	if err != nil {
		return err
	}
	htmlBody := new(bytes.Buffer)
	err = parseFS.ExecuteTemplate(htmlBody, "htmlBody", data)
	if err != nil {
		return err
	}
	//设置邮件具体内容
	message := mail.NewMessage()
	message.SetHeader("To", recipient) //设置邮件头信息
	message.SetHeader("From", m.sender)
	message.SetHeader("Subject", subject.String())
	message.SetBody("text/plain", plainBody.String())      //  设置plain-text body  纯文本正文
	message.AddAlternative("text/html", htmlBody.String()) //  设置html body 超文本语言正文

	//打开一个到SMTP服务器的链接,发送信息,关闭链接。如果超时,返回"dial tcp: i/o timeout"
	err = m.dialer.DialAndSend(message)
	if err != nil {
		return err
	}
	return nil

}

加载SMTP服务配置

使用命令行加载SMTP配置,这里的ip地址为本地地址localhost,端口号根据自己开启的fakeSMTP服务器端口号调整。

go 复制代码
flag.StringVar(&cfg.smtp.host, "smtp-host", "localhost", "SMTP host")
flag.IntVar(&cfg.smtp.port, "smtp-port", 25, "SMTP port")
//  服务器和密码用来登录smtp服务器的,这里用的本地的fakeSMTP服务器,所以填不填无所谓
flag.StringVar(&cfg.smtp.username, "smtp-username", "***", "SMTP username")
flag.StringVar(&cfg.smtp.password, "smtp-password", "***", "SMTP password")
flag.StringVar(&cfg.smtp.sender, "smtp-sender", "Greenlight <no-reply@greenlight.alexedwards.net>", "SMTP sender")

初始化mailer实例,将其加载到application配置中供handler使用

go 复制代码
app := &application{
		config: cfg,
		logger: logger,
		models: data.NewModels(db),
		mailer: mailer.New(cfg.smtp.host, cfg.smtp.port, cfg.smtp.username, cfg.smtp.password, cfg.smtp.sender),
	}

运行结果

Fake SMTP Server接收示例

具体的邮件内容

在邮件内的具体内容

相关推荐
lekami_兰2 小时前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘6 小时前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤7 小时前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt1119 小时前
AI DDD重构实践
go
Grassto2 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto4 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室5 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题5 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo
啊汉7 天前
古文观芷App搜索方案深度解析:打造极致性能的古文搜索引擎
go·软件随想
asaotomo7 天前
一款 AI 驱动的新一代安全运维代理 —— DeepSentry(深哨)
运维·人工智能·安全·ai·go