iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
Modern Web

fmt.Println("從零開始的Golang生活")系列 第 28

Day28 Gin with SMTP Server

What is an SMTP Server?

SMTP 全名為Simple Mail Transfer Protocol 簡單郵件傳輸協議,它是郵件服務器用來在電子郵件發件人和收件人之間發送、接收和/或中繼外發郵件的應用程序。

SMTP 電子郵件服務器將有一個地址(或多個地址),可由您使用的郵件客戶端或應用程序設置,通常格式為 smtp.serveraddress.com。例如,Gmail 的 SMTP 服務器主機地址為 smtp.gmail.com,Twilio SendGrid 的為 smtp.sendgrid.com。您通常可以在郵件客戶端的帳戶或設置部分中找到您的 SMTP 電子郵件服務器地址。

當您發送電子郵件時,SMTP 服務器會處理您的電子郵件,決定將消息發送到哪個服務器,並將消息中繼到該服務器。收件人的收件箱服務提供商(例如 Gmail 或 AOL)會下載郵件並將其放入收件人的收件箱中。

Is an SMTP server the same as normal server?

理論上是的,他與一般大多伺服器相同,SMTP 服務器處理要發送到另一台服務器的數據,但它具有處理與電子郵件發送、接收和中繼相關的數據的非常具體的目的。SMTP 服務器也不一定在機器上。它是一個不斷運行以期待發送新郵件的應用程序。

Gin with SMTP

這邊我們使用"net/smtp"這個官方的標準庫來實現功能。

app/service/smtp.go

package service

import (
	"github.com/joho/godotenv"
	"github.com/sirupsen/logrus"
	"ironman-2021/app/middleware"
	"net/smtp"
	"os"
)

func Send(title string, body string, to string) {
	envErr := godotenv.Load()
	if envErr != nil {
		panic(envErr)
	}

	from := os.Getenv("MAIL_USERNAME")
	pass := os.Getenv("MAIL_PASSWORD")
	port := os.Getenv("MAIL_PORT")
	server := os.Getenv("MAIL_SERVER")

	msg := "From: " + from + "\n" +
		   "To: " + to + "\n" +
		   "Subject: " + title + "\n" +
		   body

	err := smtp.SendMail(server + ":" + port,
		   smtp.PlainAuth("", from, pass, server),
		   from, []string{to}, []byte(msg))
	if err != nil{
		middleware.Logger().WithFields(logrus.Fields{
			"name": "Smtp",
		}).Error("error: ", err)
		return
	}
	middleware.Logger().WithFields(logrus.Fields{
		"name": "Smtp",
	}).Info("Send from: ", from + ", To: ", to)
}
  • 首先將會使用到的環境變數像是MAIL_USERNAME, MAIL_PASSWORD, MAIL_PORTMAIL_SERVER 都給load進function來使用。
  • 接下來則是將subject與body給寫入mail並send給Smtp 進行轉發。
  • 最後則是將轉發成功與否的訊息給寫入log當中

app/controller/user.go

這邊我們則是在創建新使用者成功時,發送一封提醒信。

// CreateUser @Summary
// @Tags user
// @version 1.0
// @produce application/json
// @param language header string true "language"
// @param register body Register true "register"
// @Success 200 string successful return value
// @Router /v1/users [post]
func (u UsersController) CreateUser(c *gin.Context) {
	t := gi18n.New()
	var form Register
	bindErr := c.BindJSON(&form)

	lan := c.Request.Header.Get("language")
	if lan == "" {
		lan = "en"
	}
	t.SetLanguage(lan)
	if bindErr == nil {
		err := service.RegisterOneUser(form.Account, form.Password, form.Email)
		if err == nil {
			service.Send("Register Notification", "Welcome to become our membership", form.Email)
			c.JSON(http.StatusOK, gin.H{
				"status": 1,
				"msg":    t.Translate(c, "Response_Success"),
				"data":   nil,
			})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{
				"status": -1,
				"msg":    "Register Failed" + err.Error(),
				"data":   nil,
			})
		}
	} else {
		c.JSON(http.StatusBadRequest, gin.H{
			"status": -1,
			"msg":    "Failed to parse register data" + bindErr.Error(),
			"data":   nil,
		})
	}
}

最後使用者則會收到一封類似下方的mail,同時我們在log也能看到發送成功的信息。

https://ithelp.ithome.com.tw/upload/images/20211013/20129737s0DU81eQL4.png

time="131310-10-10 1010:1010:1010" level=info msg="Send from: ghjjhg567@gmail.com, To: ghjjhg567@gmail.com" name=Smtp
time="131310-10-10 1010:1010:1010" level=info msg="| 200 |    5.7536937s |   192.168.144.1 | POST | /v1/users/ |"

Summary

這章節我們實作了如何以smtp轉發信件,但這邊也遇到了一個小問題,那就是我們因為要等待smtp轉發完畢才會將response回給client端,因此我們的response time變長了許多。也因此在下個章節我們要將現行架構結合async來創造一個更好的使用者體驗。

這次的程式碼我也會放在下方連結提供參考。

https://github.com/Neskem/Ironman-2021/tree/Day-28


上一篇
Day27 Gin with Colly
下一篇
Day29 Gin with Async
系列文
fmt.Println("從零開始的Golang生活")30

尚未有邦友留言

立即登入留言