跳转至

Gin

适用于 Gin(Go 1.22+)应用的集成指南。Mailexam 通过标准库 net/smtp587 端口使用 STARTTLS 作为 SMTP 接入。

前置条件

  • Mailexam 账户,以及已创建并具备 SMTP 凭据的项目
  • Go 1.22+。

从项目的欢迎邮件(或控制台)中复制:

  • YOUR_LOGIN — SMTP 登录名(例如 xxxxx);
  • YOUR_PASSWORD — SMTP 密码(与登录名成对的唯一凭据);
  • 主机 — YOUR_LOGIN.mailexam.cn登录名一致)。

1. 依赖

go mod init example.com/mailexam-gin
go get github.com/gin-gonic/gin@v1.10.0
go get github.com/joho/godotenv@v1.5.1

go.mod 文件(go get 后解析版本):

module example.com/mailexam-gin

go 1.22

require (
    github.com/gin-gonic/gin v1.10.0
    github.com/joho/godotenv v1.5.1
)

2. 环境变量

项目根目录的 .env 文件(勿将密码提交到 git):

MAILEXAM_LOGIN=YOUR_LOGIN
MAILEXAM_PASSWORD=YOUR_PASSWORD
MAILEXAM_PORT=587
MAIL_FROM=noreply@example.test

SMTP 主机:YOUR_LOGIN.mailexam.cn

发件人地址

MAIL_FROM 可以是任意测试地址——邮件进入 Mailexam,而非真实收件人。

备选端口

MAILEXAM_PORT=587

通过 client.StartTLS 发送(见 mail.go)。

MAILEXAM_PORT=2525

与 587 相同路径。

MAILEXAM_PORT=25

使用 smtp.SendMail,不启用 STARTTLS。

3. 邮件发送模块

// mail.go
package main

import (
    "crypto/tls"
    "fmt"
    "net"
    "net/smtp"
    "os"
    "strconv"
)

func sendTest(to, subject, body string) error {
    login := os.Getenv("MAILEXAM_LOGIN")
    password := os.Getenv("MAILEXAM_PASSWORD")
    if login == "" || password == "" {
        return fmt.Errorf("MAILEXAM_LOGIN and MAILEXAM_PASSWORD must be set")
    }

    port, _ := strconv.Atoi(os.Getenv("MAILEXAM_PORT"))
    if port == 0 {
        port = 587
    }

    from := os.Getenv("MAIL_FROM")
    if from == "" {
        from = "noreply@example.test"
    }
    if to == "" {
        to = "user@example.test"
    }
    if subject == "" {
        subject = "Gin + Mailexam"
    }
    if body == "" {
        body = "来自 Gin 的 Mailexam 测试"
    }

    host := login + ".mailexam.cn"
    addr := fmt.Sprintf("%s:%d", host, port)
    auth := smtp.PlainAuth("", login, password, host)

    msg := []byte(fmt.Sprintf(
        "From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n%s",
        from, to, subject, body,
    ))

    if port == 587 || port == 2525 {
        return sendWithSTARTTLS(addr, host, auth, from, []string{to}, msg)
    }

    return smtp.SendMail(addr, auth, from, []string{to}, msg)
}

func sendWithSTARTTLS(addr, host string, auth smtp.Auth, from string, to []string, msg []byte) error {
    conn, err := net.Dial("tcp", addr)
    if err != nil {
        return err
    }
    defer conn.Close()

    client, err := smtp.NewClient(conn, host)
    if err != nil {
        return err
    }
    defer client.Close()

    if ok, _ := client.Extension("STARTTLS"); ok {
        if err = client.StartTLS(&tls.Config{ServerName: host}); err != nil {
            return err
        }
    }

    if auth != nil {
        if err = client.Auth(auth); err != nil {
            return err
        }
    }

    if err = client.Mail(from); err != nil {
        return err
    }
    for _, rcpt := range to {
        if err = client.Rcpt(rcpt); err != nil {
            return err
        }
    }

    w, err := client.Data()
    if err != nil {
        return err
    }
    if _, err = w.Write(msg); err != nil {
        return err
    }
    if err = w.Close(); err != nil {
        return err
    }

    return client.Quit()
}

4. Gin 路由

// main.go
package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/joho/godotenv"
)

type sendRequest struct {
    To      string `json:"to"`
    Subject string `json:"subject"`
    Body    string `json:"body"`
}

func main() {
    _ = godotenv.Load()

    r := gin.Default()

    r.POST("/mail/test", func(c *gin.Context) {
        var req sendRequest
        _ = c.ShouldBindJSON(&req)

        if err := sendTest(req.To, req.Subject, req.Body); err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, gin.H{"status": "ok"})
    })

    if err := r.Run("127.0.0.1:8080"); err != nil {
        panic(err)
    }
}

启动并验证:

go run .
curl -X POST http://127.0.0.1:8080/mail/test \
  -H 'Content-Type: application/json' \
  -d '{"to":"user@example.test","subject":"测试","body":"你好"}'

邮件将出现在 Mailexam 控制台 → 您的项目 → 收件箱。

5. 本地开发与 CI

环境 建议
local .env + godotenv.Load()
CI GitLab CI/CD Variables 中的密钥 MAILEXAM_LOGINMAILEXAM_PASSWORD

.gitlab-ci.yml 示例:

variables:
  MAILEXAM_LOGIN: $MAILEXAM_LOGIN
  MAILEXAM_PASSWORD: $MAILEXAM_PASSWORD
  MAILEXAM_PORT: "587"
  MAIL_FROM: "noreply@example.test"

集成测试后,通过 Mailexam API 验证投递。

6. 常见问题

TLS 或认证失败

  • 代码中 host{login}.mailexam.cnPlainAuth 中 — 同一登录名。
  • 登录名和密码为同一项目的邮件成对凭据

587 端口

  • 需要 STARTTLS(sendWithSTARTTLS),不能直接使用无 TLS 的 smtp.SendMail

控制台中看不到邮件

  • 查看同一 Mailexam 项目的收件箱。
  • 使用 GIN_MODE=debug 运行,检查 500 响应中的错误信息。

参见