Fiber
Email checker for Go Fiber. Email verification in Fiber HTTP handlers.
將 EmailVerify 集成到您的 Fiber 應用程序中,實現超快速的电子邮件驗證。本指南涵蓋性能優化、WebSocket 支持和生產監控。
安裝
安裝 Fiber 框架和 EmailVerify 依賴項。
go get github.com/gofiber/fiber/v2
go get github.com/emailverify/emailverify-go
go get github.com/go-playground/validator/v10
go get github.com/go-redis/redis/v8
go get gorm.io/gorm
go get github.com/prometheus/client_golangrequire (
github.com/gofiber/fiber/v2 v2.50.0
github.com/emailverify/emailverify-go v0.1.0
github.com/go-playground/validator/v10 v10.15.0
github.com/go-redis/redis/v8 v8.11.5
gorm.io/gorm v1.25.0
github.com/prometheus/client_golang v1.17.0
)配置
Fiber 應用程序设置
配置您的 Fiber 應用程序以實現性能優化:
package config
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/helmet"
)
type AppConfig struct {
APIKey string
DatabaseURL string
RedisURL string
Port string
}
func NewApp(cfg *AppConfig) *fiber.App {
app := fiber.New(fiber.Config{
// 性能優化
Prefork: true,
Network: fiber.NetworkTCP,
// 流式請求體大小
StreamRequestBody: true,
BodyLimit: 10 * 1024 * 1024, // 10 MB
// 禁用 keep-alive
DisableKeepalive: false,
// 大小寫敏感的路由
CaseSensitive: true,
// 严格的路由
StrictRoute: false,
})
// 中間件
app.Use(helmet.New())
app.Use(compress.New(compress.Config{
Level: compress.LevelBestSpeed,
}))
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowMethods: "GET,POST,PUT,DELETE,OPTIONS",
}))
return app
}環境配置
從環境加載配置:
package config
import (
"os"
"log"
)
func LoadConfig() *AppConfig {
apiKey := os.Getenv("EMAILVERIFY_API_KEY")
if apiKey == "" {
log.Fatal("EMAILVERIFY_API_KEY 是必需的")
}
return &AppConfig{
APIKey: apiKey,
DatabaseURL: os.Getenv("DATABASE_URL"),
RedisURL: os.Getenv("REDIS_URL"),
Port: getEnv("PORT", "3000"),
}
}
func getEnv(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}中間件
电子邮件驗證中間件
為請求日志記录和驗證创建自定義中間件:
package middleware
import (
"github.com/gofiber/fiber/v2"
"log/slog"
"time"
)
func Logger() fiber.Handler {
return func(c *fiber.Ctx) error {
start := time.Now()
// 處理請求
err := c.Next()
// 計算执行時間
stop := time.Now()
latency := stop.Sub(start)
// 記录請求詳情
slog.Info("HTTP 請求",
"method", c.Method(),
"path", c.Path(),
"status", c.Response().StatusCode(),
"latency_ms", latency.Milliseconds(),
)
return err
}
}
func ErrorHandler() fiber.ErrorHandler {
return func(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
return c.Status(code).JSON(fiber.Map{
"error": err.Error(),
"code": code,
})
}
}CORS 中間件
為跨源請求配置 CORS:
package middleware
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
)
func SetupCORS(app *fiber.App) {
app.Use(cors.New(cors.Config{
AllowOrigins: "https://example.com,https://api.example.com",
AllowMethods: "GET,POST,PUT,DELETE,OPTIONS",
AllowHeaders: "Content-Type,Authorization",
ExposeHeaders: "Content-Length,X-RateLimit-Limit",
MaxAge: 3600,
AllowCredentials: true,
}))
}路由
單個电子邮件驗證路由
定義用于驗證單個电子邮件的路由:
package routes
import (
"github.com/gofiber/fiber/v2"
"emailverify-app/handlers"
"emailverify-app/services"
)
type VerifyEmailRequest struct {
Email string `json:"email" validate:"required,email"`
}
type VerifyEmailResponse struct {
Email string `json:"email"`
Status string `json:"status"`
Score float64 `json:"score"`
Details interface{} `json:"details"`
Cached bool `json:"cached"`
}
func SetupVerifyRoutes(app *fiber.App, service *services.EmailVerificationService) {
api := app.Group("/api")
verify := api.Group("/verify")
verify.Post("/email", func(c *fiber.Ctx) error {
var req VerifyEmailRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "無效的請求",
})
}
// 驗證請求
if err := validate.Struct(req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "驗證失败",
"details": err.Error(),
})
}
// 驗證电子邮件
result, cached, err := service.VerifyEmail(c.Context(), req.Email)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "驗證失败",
})
}
response := VerifyEmailResponse{
Email: req.Email,
Status: result.Status,
Score: result.Score,
Details: result.Details,
Cached: cached,
}
return c.JSON(response)
})
}批量电子邮件驗證路由
為批量驗證创建路由:
type BulkVerifyRequest struct {
Emails []string `json:"emails" validate:"required,min=1,max=1000,dive,email"`
}
type BulkVerifyResponse struct {
Total int `json:"total"`
Verified int `json:"verified"`
Failed int `json:"failed"`
Results []VerifyEmailResponse `json:"results"`
Duration string `json:"duration"`
}
func SetupBulkRoutes(app *fiber.App, service *services.EmailVerificationService) {
api := app.Group("/api")
verify := api.Group("/verify")
verify.Post("/bulk", func(c *fiber.Ctx) error {
var req BulkVerifyRequest
start := time.Now()
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "無效的請求",
})
}
// 驗證請求
if err := validate.Struct(req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "驗證失败",
})
}
results := make([]VerifyEmailResponse, 0, len(req.Emails))
failed := 0
for _, email := range req.Emails {
result, cached, err := service.VerifyEmail(c.Context(), email)
if err != nil {
failed++
continue
}
results = append(results, VerifyEmailResponse{
Email: email,
Status: result.Status,
Score: result.Score,
Details: result.Details,
Cached: cached,
})
}
response := BulkVerifyResponse{
Total: len(req.Emails),
Verified: len(results),
Failed: failed,
Results: results,
Duration: time.Since(start).String(),
}
return c.JSON(response)
})
}驗證
自定義驗證器
使用自定義規則擴展驗證:
package validators
import (
"github.com/go-playground/validator/v10"
"strings"
)
type CustomValidator struct {
validator *validator.Validate
}
func NewValidator() *CustomValidator {
v := validator.New()
v.RegisterValidationFunc("not_disposable", func(fl validator.FieldLevel) bool {
email := fl.Field().String()
domain := strings.Split(email, "@")[1]
blockedDomains := map[string]bool{
"tempmail.com": true,
"10minutemail.com": true,
"guerrillamail.com": true,
"mailinator.com": true,
}
return !blockedDomains[domain]
})
v.RegisterValidationFunc("corporate_email", func(fl validator.FieldLevel) bool {
email := fl.Field().String()
domain := strings.Split(email, "@")[1]
// 僅允許特定的企業域名
allowedDomains := map[string]bool{
"company.com": true,
}
return allowedDomains[domain]
})
return &CustomValidator{validator: v}
}
func (cv *CustomValidator) Validate(i interface{}) error {
return cv.validator.Struct(i)
}數据庫集成
GORM 與 Fiber
集成 GORM 用于數据庫持久化:
package database
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
"time"
)
type VerificationResult struct {
ID uint `gorm:"primaryKey"`
Email string `gorm:"uniqueIndex;not null;type:varchar(254)"`
Status string `gorm:"not null;type:varchar(50)"`
Score float64
Disposable bool
SmtpValid bool
FormatValid bool
EmailProvider string
RiskLevel string
VerifiedAt time.Time
CachedAt time.Time `gorm:"autoUpdateTime:milli"`
}
func NewDatabase(dsn string) (*gorm.DB, error) {
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
// 啟用連接池來提高性能
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxOpenConns(25)
sqlDB.SetMaxIdleConns(5)
sqlDB.SetConnMaxLifetime(5 * time.Minute)
// 自動遷移模型
if err := db.AutoMigrate(&VerificationResult{}); err != nil {
return nil, err
}
return db, nil
}緩存
內存和 Redis 緩存
實現內存和 Redis 的多層緩存:
package cache
import (
"context"
"encoding/json"
"github.com/go-redis/redis/v8"
"sync"
"time"
)
type MemoryCache struct {
data map[string]CacheEntry
mu sync.RWMutex
ttl time.Duration
}
type CacheEntry struct {
Value interface{}
ExpiresAt time.Time
}
type HybridCache struct {
memory *MemoryCache
redis *redis.Client
}
func NewHybridCache(redisURL string) (*HybridCache, error) {
opts, _ := redis.ParseURL(redisURL)
client := redis.NewClient(opts)
return &HybridCache{
memory: &MemoryCache{
data: make(map[string]CacheEntry),
ttl: 5 * time.Minute,
},
redis: client,
}, nil
}
func (hc *HybridCache) Get(ctx context.Context, key string) (interface{}, error) {
// 先檢查內存緩存
hc.memory.mu.RLock()
entry, exists := hc.memory.data[key]
hc.memory.mu.RUnlock()
if exists && time.Now().Before(entry.ExpiresAt) {
return entry.Value, nil
}
// 檢查 Redis
val, err := hc.redis.Get(ctx, key).Result()
if err == nil {
var data interface{}
json.Unmarshal([]byte(val), &data)
// 填充內存緩存
hc.memory.mu.Lock()
hc.memory.data[key] = CacheEntry{
Value: data,
ExpiresAt: time.Now().Add(hc.memory.ttl),
}
hc.memory.mu.Unlock()
return data, nil
}
return nil, err
}
func (hc *HybridCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
// 在內存緩存中设置
hc.memory.mu.Lock()
hc.memory.data[key] = CacheEntry{
Value: value,
ExpiresAt: time.Now().Add(hc.memory.ttl),
}
hc.memory.mu.Unlock()
// 在 Redis 中设置
data, _ := json.Marshal(value)
return hc.redis.Set(ctx, key, data, ttl).Err()
}性能優化
連接池和優化
優化以獲得高吞吐量:
package config
import (
"github.com/gofiber/fiber/v2"
"net"
"time"
)
func OptimizePerformance(app *fiber.App) {
// 調整網絡设置
app.Config.Network = fiber.NetworkTCP4
app.Config.Concurrency = 256 * 1024 // 最大並發連接數
// TCP 調整
app.Listen(":3000", fiber.ListenConfig{
ListenerNetwork: "tcp",
SocketShutdown: true,
TcpKeepAlive: 30 * time.Second,
})
// 請求體優化
app.Config.BodyLimit = 50 * 1024 * 1024 // 50 MB
app.Config.ReadBufferSize = 16 * 1024 // 16 KB
app.Config.WriteBufferSize = 16 * 1024 // 16 KB
// Keep-alive 设置
app.Config.IdleTimeout = 75 * time.Second
app.Config.ReadTimeout = 15 * time.Second
app.Config.WriteTimeout = 15 * time.Second
}錯誤處理
全面的錯誤處理
定義一致的錯誤響應:
package errors
import (
"github.com/gofiber/fiber/v2"
)
type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
func BadRequest(c *fiber.Ctx, message string) error {
return c.Status(fiber.StatusBadRequest).JSON(APIError{
Code: fiber.StatusBadRequest,
Message: message,
})
}
func NotFound(c *fiber.Ctx, message string) error {
return c.Status(fiber.StatusNotFound).JSON(APIError{
Code: fiber.StatusNotFound,
Message: message,
})
}
func InternalError(c *fiber.Ctx, message string) error {
return c.Status(fiber.StatusInternalServerError).JSON(APIError{
Code: fiber.StatusInternalServerError,
Message: message,
})
}測試
使用 httptest 進行集成測試
測試您的路由和處理器:
package handlers
import (
"bytes"
"encoding/json"
"testing"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert"
)
func TestVerifyEmail(t *testing.T) {
tests := []struct {
name string
email string
expectedStatus int
}{
{
name: "有效的电子邮件",
email: "test@example.com",
expectedStatus: fiber.StatusOK,
},
{
name: "無效的电子邮件格式",
email: "invalid",
expectedStatus: fiber.StatusBadRequest,
},
{
name: "空电子邮件",
email: "",
expectedStatus: fiber.StatusBadRequest,
},
}
app := fiber.New()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
body := VerifyEmailRequest{Email: tt.email}
bodyBytes, _ := json.Marshal(body)
req := fiber.AcquireRequest()
req.Header.SetContentType(fiber.MIMEApplicationJSON)
req.SetBodyStream(bytes.NewReader(bodyBytes), len(bodyBytes))
// 測試端点
// assert.Equal(t, tt.expectedStatus, statusCode)
})
}
}WebSocket 支持
實時电子邮件驗證
為實時驗證實現 WebSocket:
package routes
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
"emailverify-app/services"
)
func SetupWebSocketRoutes(app *fiber.App, service *services.EmailVerificationService) {
ws := app.Group("/ws")
ws.Get("/verify/:email", websocket.New(func(c *websocket.Conn) {
email := c.Params("email")
// 驗證电子邮件
result, cached, err := service.VerifyEmail(c.Context(), email)
if err != nil {
c.WriteJSON(fiber.Map{
"error": err.Error(),
})
return
}
// 通過 WebSocket 發送結果
c.WriteJSON(fiber.Map{
"email": email,
"status": result.Status,
"score": result.Score,
"cached": cached,
})
// 優雅關闭
c.Close()
}))
}監控
Prometheus 指標
添加 Prometheus 監控以實現生產可观測性:
package monitoring
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
EmailVerifications = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "email_verifications_duration_seconds",
Help: "电子邮件驗證耗時(秒)",
Buckets: []float64{0.001, 0.01, 0.1, 1.0},
},
[]string{"status"},
)
VerificationErrors = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "verification_errors_total",
Help: "驗證錯誤總數",
},
[]string{"error_type"},
)
CacheHits = promauto.NewCounter(
prometheus.CounterOpts{
Name: "cache_hits_total",
Help: "緩存命中總數",
},
)
CacheMisses = promauto.NewCounter(
prometheus.CounterOpts{
Name: "cache_misses_total",
Help: "緩存未命中總數",
},
)
)生產部署
Prefork 模式
啟用 prefork 模式以實現多進程性能:
func main() {
app := fiber.New(fiber.Config{
Prefork: true,
Network: fiber.NetworkTCP,
})
// 设置路由
setupRoutes(app)
// 在生產環境中使用 TLS 監听
app.ListenTLS(":443", "/path/to/cert.pem", "/path/to/key.pem", fiber.ListenConfig{
GracefulShutdown: true,
})
}Docker 配置
生產就绪的 Dockerfile:
# 構建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o app .
# 運行時阶段
FROM alpine:latest
RUN apk add --no-cache ca-certificates
WORKDIR /app
COPY --from=builder /build/app .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget -q -O /dev/null http://localhost:3000/health || exit 1
CMD ["./app"]docker-compose.yml
完整的堆栈配置:
version: '3.8'
services:
api:
build: .
ports:
- "3000:3000"
environment:
EMAILVERIFY_API_KEY: ${EMAILVERIFY_API_KEY}
DATABASE_URL: postgres://user:password@db:5432/emailverify
REDIS_URL: redis://redis:6379/0
depends_on:
- db
- redis
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: emailverify
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
volumes:
postgres_data:
redis_data: