最近Golangでメール送信処理を書くことがあったのだけど、あまり事情を知らなかったのでまとめた。
golang+SMTPでメールを送る
Goには標準ライブラリでnet/smtp
というのがある。
smtp package - net/smtp - Go Packages
これは名前の通りGoからSMTPでメールを送信するためのライブラリなのだけど、例えばヘッダとボディの間には空行を一行自分で挟まないといけないとか、素朴すぎて結構辛い。 さすがに2023年にもなってさすがにそういうことはやりたくないので、もう少しいいやつないかなと探して、今回は以下のライブラリを使った。
これはそこそこ高機能だと思う。少なくとも自分でヘッダ部とボディの間に空行を入れる、みたいなことをしなくてもいい。middlewareを差し込めるようになっていて、middlewareによって挙動を少し変える、みたいな最近ぽいこともできるのもよい(そういう場面がどれくらいあるかは置いといて)。あと最近もメンテされているという点もよい。
SMTPでメール送るコードはこういう感じ。
package main import ( "log" "mime" "os" "strconv" "github.com/wneessen/go-mail" ) func main() { msg := mail.NewMsg() host := os.Getenv("SMTP_HOST") if host == "" { log.Fatal("SMTP_HOST required") } port, err := strconv.Atoi(os.Getenv("SMTP_PORT")) if err != nil { log.Fatal(err) } if err := msg.From("hoge@example.test"); err != nil { log.Fatal(err) } if err := msg.To("fuga@example.test"); err != nil { log.Fatal(err) } msg.Subject(mime.BEncoding.Encode("UTF-8", "こんにちはこんにちは")) msg.SetBodyString(mail.TypeTextPlain, "ようこそこんにちは") c, err := mail.NewClient(host, mail.WithPort(port)) if err != nil { log.Fatal(err) } if err := c.DialAndSend(msg); err != nil { log.Fatal(err) } }
こういう感じで動く。Subjectはmime.BEncoding
したりする必要がある。あとSMTPのホストとかポートも環境変数で渡せるようにしておくほうが使いやすいけど、この辺はそれぞれの事情による。
送信されるメールを確認する
メールを送信するコードを書いたり、送信用のメールのテンプレートを追加したような時に、見た目を確認するために実際に自分なりクローズドな何かにメールを送る、というのをやったことがある人は結構いると思う。こういう作業でミスしないように慎重に送信テストするぞ・・・とか言ってストレスMAXになったりしたことありませんか。僕はある。ローカルの環境にメールサーバーを立てておいて、そこに送って確認したらまあいいのだけど、そんな面倒なことはしたくない・・・という人におすすめなのがMailhog
というテスト用のSMTPサーバーを立てる方法。
これを自前でビルドするとかではなくて、DockerHubに既にイメージが存在するのでそれを使う。
これがいいのはWebUIがついている点で、これで立てられたテスト用のSMTPサーバーにメールを送ると、送られたメールをそのWebUIで確認できる。
開発時にはdocker compose
でアプリケーションと同時に立てると、アプリケーションのメール送信処理をテストする仕組みを簡単につくることができる。
docker-compose.ymlの該当部分はこういう感じになる。
version: '3.8' services: app: build: context: . command: [ ] # 任意のコマンド env: SMTP_HOST: 'mail' SMTP_PORT: '1025' mail: image: mailhog/mailhog:latest ports: - "8025:8025" - "1025:1025"
mailhogはデフォルトだとSMTPサーバーは1025番で、WebUIは8025番で受けているので、必要であればポートフォワードするとよい。
前述のメール送信のコードにも書いたけど、アプリケーションにはSMTP_HOST
やSMTP_PORT
を環境変数で渡すようにしておけば、ローカル開発ではmail
コンテナの1025番にメールを送信して、本番環境では任意のホスト/ポートを設定する、というようにできる。
あとはメールを送信する仕組みを実際に動かして、WebUIで確認したらよい。個人的には、本当に送信されることがないよう、RFC2606で予約済みのTLDである.test
みたいなドメインにテストメールを送るようにしている(これが正しい使い方かは微妙なラインかもしれない)
https://tex2e.github.io/rfc-translater/html/rfc2606.html
まとめ
近年だとメールを送ることあまりないだろうし、あってもAWS SNSみたいなマネージドサービスを使ってSDK経由でメール送信する、みたいなことの方が多いと思うけど、もしもSMTP経由でメール送信することになったら参考にしてほしい。あとMailhogは便利。