Golang使用smtp.SendMail时出现“崩溃”问题如何解决

Golang使用smtp.SendMail时出现“崩溃”问题如何解决 我有一个使用Golang作为"Apache"的网站。

http.ListenAndServe(":9090", nil) <----- 启动网站

在这个网站中,我有一个表单,使用smtp.SendMail提交邮件。

  1. 发送"有效"邮件地址时工作完美

  2. 发送"有效"邮件地址时有时返回"550 5.7.1 SPF失败。域名test.com不允许从(IP地址)发送邮件"

  3. 发送"无意义"邮件地址(test)失败并报告"501 5.1.7 发件人地址语法错误"

 mailfrom = "test@company.se" <--- 有效的邮件地址

 auth := smtp.PlainAuth("", "sibert@gmail.com", "password", "smtp.gmail.com")
 from := (mailfrom)
 to := []string{"sibert@gmail.com"}
 msg := []byte("From: " + mailfrom + "\r\n" +
 	"To: support@company.com\r\n" +
 	"Subject: Test\r\n" +
 	"testmail\r\n" +
 	(mailfrom))
 err := smtp.SendMail("smtp.gmail.com:587", auth, from, to, msg)
 if err != nil {
 	log.Fatal(err)
 }
}

有两个问题:

  1. 如何捕获这个错误并向用户发送"抱歉,邮件未送达"?
  2. 当Go不喜欢邮件地址时,如何防止"http.ListenAndServe"崩溃?

更多关于Golang使用smtp.SendMail时出现“崩溃”问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

好的,记录错误或更少。

更多关于Golang使用smtp.SendMail时出现“崩溃”问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


不要使用致命日志记录,它意味着程序会发生恐慌。

谢谢!这解决了问题。

if err != nil {
	return ("sorry.html")
}
return ("thanks.html")

NobbZ:

不要使用致命日志记录,这会导致程序恐慌。

那我应该怎么做?

我不清楚这个函数原本应该做什么,但对我来说,当它应该发送邮件时却返回一个HTML文档的名称,这似乎不太对劲。

此外,通常你会希望记录错误及其原因,因为用户可能会试图寻找你软件中的弱点。

最后但同样重要的是,return 不是一个函数,不需要加括号……

func main() {
    fmt.Println("hello world")
}

针对您的问题,以下是解决方案和示例代码:

问题1:捕获错误并向用户返回友好消息

package main

import (
    "fmt"
    "log"
    "net/http"
    "net/smtp"
    "strings"
)

func sendEmailHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }

    mailfrom := "test@company.se"
    auth := smtp.PlainAuth("", "sibert@gmail.com", "password", "smtp.gmail.com")
    from := mailfrom
    to := []string{"sibert@gmail.com"}
    msg := []byte("From: " + mailfrom + "\r\n" +
        "To: support@company.com\r\n" +
        "Subject: Test\r\n" +
        "testmail\r\n" +
        mailfrom)

    err := smtp.SendMail("smtp.gmail.com:587", auth, from, to, msg)
    if err != nil {
        log.Printf("邮件发送失败: %v", err)
        
        // 检查错误类型并返回友好消息
        errorMsg := "抱歉,邮件未送达"
        if strings.Contains(err.Error(), "550") {
            errorMsg = "抱歉,邮件发送失败:SPF验证问题"
        } else if strings.Contains(err.Error(), "501") {
            errorMsg = "抱歉,邮件地址格式不正确"
        }
        
        http.Error(w, errorMsg, http.StatusInternalServerError)
        return
    }

    fmt.Fprintf(w, "邮件发送成功")
}

func main() {
    http.HandleFunc("/send-email", sendEmailHandler)
    log.Println("服务器启动在 :9090")
    log.Fatal(http.ListenAndServe(":9090", nil))
}

问题2:防止服务器崩溃的完整解决方案

package main

import (
    "fmt"
    "log"
    "net/http"
    "net/smtp"
    "strings"
)

func validateEmail(email string) bool {
    // 简单的邮箱格式验证
    return strings.Contains(email, "@") && strings.Contains(email, ".")
}

func sendEmailHandler(w http.ResponseWriter, r *http.Request) {
    // 使用defer和recover捕获panic
    defer func() {
        if err := recover(); err != nil {
            log.Printf("处理请求时发生panic: %v", err)
            http.Error(w, "服务器内部错误", http.StatusInternalServerError)
        }
    }()

    if r.Method != "POST" {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }

    // 从表单获取邮件地址
    r.ParseForm()
    mailfrom := r.FormValue("email")
    
    // 验证邮件地址格式
    if !validateEmail(mailfrom) {
        http.Error(w, "无效的邮件地址格式", http.StatusBadRequest)
        return
    }

    auth := smtp.PlainAuth("", "sibert@gmail.com", "password", "smtp.gmail.com")
    from := mailfrom
    to := []string{"sibert@gmail.com"}
    
    msg := []byte("From: " + mailfrom + "\r\n" +
        "To: support@company.com\r\n" +
        "Subject: Test\r\n" +
        "Content-Type: text/plain; charset=UTF-8\r\n" +
        "\r\n" +
        "testmail\r\n" +
        "发件人: " + mailfrom)

    err := smtp.SendMail("smtp.gmail.com:587", auth, from, to, msg)
    if err != nil {
        log.Printf("SMTP错误: %v", err)
        
        // 分类处理不同的SMTP错误
        errorMsg := "抱歉,邮件发送失败"
        errStr := err.Error()
        
        switch {
        case strings.Contains(errStr, "550"):
            errorMsg = "邮件发送失败:SPF验证未通过"
        case strings.Contains(errStr, "501"):
            errorMsg = "邮件地址格式不正确"
        case strings.Contains(errStr, "535"):
            errorMsg = "认证失败,请检查账号密码"
        case strings.Contains(errStr, "421"):
            errorMsg = "邮件服务器暂时不可用"
        }
        
        http.Error(w, errorMsg, http.StatusInternalServerError)
        return
    }

    fmt.Fprintf(w, "邮件发送成功")
}

func main() {
    // 设置全局panic处理
    defer func() {
        if err := recover(); err != nil {
            log.Printf("服务器发生panic: %v", err)
        }
    }()

    http.HandleFunc("/send-email", sendEmailHandler)
    
    log.Println("服务器启动在 :9090")
    err := http.ListenAndServe(":9090", nil)
    if err != nil {
        log.Fatalf("服务器启动失败: %v", err)
    }
}

增强的错误处理版本

package main

import (
    "fmt"
    "log"
    "net/http"
    "net/smtp"
    "regexp"
)

type EmailService struct {
    smtpHost string
    smtpPort string
    auth     smtp.Auth
}

func NewEmailService(username, password, host, port string) *EmailService {
    auth := smtp.PlainAuth("", username, password, host)
    return &EmailService{
        smtpHost: host,
        smtpPort: port,
        auth:     auth,
    }
}

func (es *EmailService) SendEmail(from, to, subject, body string) error {
    // 验证邮件地址
    if !isValidEmail(from) || !isValidEmail(to) {
        return fmt.Errorf("无效的邮件地址格式")
    }

    msg := []byte(fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s",
        from, to, subject, body))

    server := fmt.Sprintf("%s:%s", es.smtpHost, es.smtpPort)
    return smtp.SendMail(server, es.auth, from, []string{to}, msg)
}

func isValidEmail(email string) bool {
    emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
    matched, _ := regexp.MatchString(emailRegex, email)
    return matched
}

func main() {
    emailService := NewEmailService("sibert@gmail.com", "password", "smtp.gmail.com", "587")
    
    http.HandleFunc("/send-email", func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("Handler panic: %v", err)
                http.Error(w, "内部服务器错误", http.StatusInternalServerError)
            }
        }()

        if r.Method != "POST" {
            http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
            return
        }

        r.ParseForm()
        from := r.FormValue("from")
        to := r.FormValue("to")
        subject := r.FormValue("subject")
        body := r.FormValue("body")

        err := emailService.SendEmail(from, to, subject, body)
        if err != nil {
            log.Printf("邮件发送错误: %v", err)
            http.Error(w, "抱歉,邮件发送失败: "+err.Error(), http.StatusInternalServerError)
            return
        }

        fmt.Fprintf(w, "邮件发送成功")
    })

    log.Println("服务器运行在 :9090")
    log.Fatal(http.ListenAndServe(":9090", nil))
}

这些解决方案通过以下方式解决问题:

  1. 使用 deferrecover() 捕获 panic 防止服务器崩溃
  2. 对 SMTP 错误进行分类处理,返回用户友好的错误信息
  3. 添加邮件地址格式验证,提前拦截无效地址
  4. 使用结构体封装邮件服务,提高代码可维护性
回到顶部