golang-http-frameworks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go HTTP Frameworks & REST APIs

Go HTTP框架与REST API

Overview

概述

Go provides exceptional HTTP capabilities starting with the standard library's
net/http
package. Go 1.22+ introduced enhanced pattern routing in
ServeMux
, making stdlib viable for many applications. For more complex needs, frameworks like Chi, Gin, Echo, and Fiber offer additional features while maintaining Go's simplicity and performance.
Key Features:
  • 🌐 net/http: Production-ready standard library with Go 1.22+ routing
  • 🎯 Chi: Lightweight, stdlib-compatible router with middleware chains
  • Gin: High-performance framework with binding and validation
  • 🛡️ Echo: Type-safe, enterprise framework with OpenAPI support
  • 🚀 Fiber: Express.js-inspired framework with WebSocket support
  • 🔧 Middleware: Composable request/response processing
  • Validation: Struct tag-based request validation
  • 🧪 Testing: httptest.Server for comprehensive integration tests
Go 从标准库的
net/http
包开始就提供了出色的HTTP处理能力。Go 1.22+版本在
ServeMux
中引入了增强的模式路由,使得标准库足以满足许多应用的需求。对于更复杂的场景,Chi、Gin、Echo和Fiber等框架在保持Go简洁性和高性能的同时,提供了更多附加功能。
核心特性:
  • 🌐 net/http:Go 1.22+版本路由支持的生产级标准库
  • 🎯 Chi:轻量级、兼容标准库的路由器,支持中间件链
  • Gin:具备绑定和验证功能的高性能框架
  • 🛡️ Echo:支持OpenAPI的类型安全企业级框架
  • 🚀 Fiber:受Express.js启发、支持WebSocket的框架
  • 🔧 中间件:可组合的请求/响应处理机制
  • 验证:基于结构体标签的请求验证
  • 🧪 测试:使用httptest.Server进行全面集成测试

When to Use This Skill

何时使用该技能

Activate this skill when:
  • Building RESTful APIs or web services
  • Choosing appropriate HTTP framework for project requirements
  • Implementing authentication or authorization middleware
  • Designing REST endpoint patterns and validation
  • Testing HTTP handlers and middleware chains
  • Optimizing API performance and response times
  • Migrating between HTTP frameworks
在以下场景中启用此技能:
  • 构建RESTful API或Web服务
  • 根据项目需求选择合适的HTTP框架
  • 实现认证或授权中间件
  • 设计REST端点模式与验证规则
  • 测试HTTP处理器和中间件链
  • 优化API性能和响应时间
  • 在不同HTTP框架之间进行迁移

Framework Selection Guide

框架选择指南

net/http (Standard Library) - Go 1.22+

net/http(标准库)- Go 1.22+

Use When:
  • Building simple to moderate complexity APIs
  • Avoiding external dependencies is priority
  • Need maximum compatibility and long-term stability
  • Team prefers explicit over implicit patterns
Strengths:
  • Zero dependencies, part of Go standard library
  • Go 1.22+ pattern routing with path parameters
  • Excellent performance and stability
  • Extensive ecosystem compatibility
  • No framework lock-in
Limitations:
  • More verbose middleware composition
  • Manual request validation
  • No built-in binding or rendering
Example:
go
package main

import (
    "encoding/json"
    "net/http"
    "log"
)

// Go 1.22+ pattern routing
func main() {
    mux := http.NewServeMux()

    // Path parameters with {param} syntax
    mux.HandleFunc("GET /users/{id}", getUserHandler)
    mux.HandleFunc("POST /users", createUserHandler)
    mux.HandleFunc("GET /users", listUsersHandler)

    // Middleware wrapping
    handler := loggingMiddleware(mux)

    log.Fatal(http.ListenAndServe(":8080", handler))
}

func getUserHandler(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id") // Go 1.22+ path parameter extraction

    user := User{ID: id, Name: "John Doe"}

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}
适用场景:
  • 构建简单到中等复杂度的API
  • 优先考虑避免外部依赖
  • 需要最高兼容性和长期稳定性
  • 团队偏好显式而非隐式模式
优势:
  • 零依赖,属于Go标准库的一部分
  • Go 1.22+版本支持带路径参数的模式路由
  • 卓越的性能和稳定性
  • 广泛的生态系统兼容性
  • 无框架锁定
局限性:
  • 中间件组合更繁琐
  • 需要手动进行请求验证
  • 无内置绑定或渲染功能
示例:
go
package main

import (
    "encoding/json"
    "net/http"
    "log"
)

// Go 1.22+ pattern routing
func main() {
    mux := http.NewServeMux()

    // Path parameters with {param} syntax
    mux.HandleFunc("GET /users/{id}", getUserHandler)
    mux.HandleFunc("POST /users", createUserHandler)
    mux.HandleFunc("GET /users", listUsersHandler)

    // Middleware wrapping
    handler := loggingMiddleware(mux)

    log.Fatal(http.ListenAndServe(":8080", handler))
}

func getUserHandler(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id") // Go 1.22+ path parameter extraction

    user := User{ID: id, Name: "John Doe"}

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

Chi - Lightweight Router

Chi - 轻量级路由器

Use When:
  • Want stdlib-compatible router with better ergonomics
  • Need clean middleware composition
  • Prefer explicit over magic patterns
  • Building moderate to complex routing structures
Strengths:
  • 100% compatible with
    net/http
  • Excellent middleware ecosystem
  • Route grouping and nesting
  • Context-based parameter passing
  • Minimal performance overhead
Installation:
bash
go get -u github.com/go-chi/chi/v5
Example:
go
package main

import (
    "encoding/json"
    "net/http"
    "github.com/go-chi/chi/v5"
    "github.com/go-chi/chi/v5/middleware"
)

func main() {
    r := chi.NewRouter()

    // Built-in middleware
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    r.Use(middleware.RequestID)

    // Route grouping
    r.Route("/api/v1", func(r chi.Router) {
        r.Route("/users", func(r chi.Router) {
            r.Get("/", listUsers)
            r.Post("/", createUser)

            r.Route("/{userID}", func(r chi.Router) {
                r.Use(UserContext) // Middleware for nested routes
                r.Get("/", getUser)
                r.Put("/", updateUser)
                r.Delete("/", deleteUser)
            })
        })
    })

    http.ListenAndServe(":8080", r)
}

func UserContext(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        userID := chi.URLParam(r, "userID")
        // Load user from database, set in context
        ctx := context.WithValue(r.Context(), "user", userID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func getUser(w http.ResponseWriter, r *http.Request) {
    userID := r.Context().Value("user").(string)
    // Return user data
    json.NewEncoder(w).Encode(map[string]string{"id": userID})
}
适用场景:
  • 想要兼容标准库且更易用的路由器
  • 需要清晰的中间件组合方式
  • 偏好显式而非魔法模式
  • 构建中等至复杂的路由结构
优势:
  • 100%兼容
    net/http
  • 优秀的中间件生态系统
  • 支持路由分组和嵌套
  • 基于上下文的参数传递
  • 极低的性能开销
安装:
bash
go get -u github.com/go-chi/chi/v5
示例:
go
package main

import (
    "encoding/json"
    "net/http"
    "github.com/go-chi/chi/v5"
    "github.com/go-chi/chi/v5/middleware"
)

func main() {
    r := chi.NewRouter()

    // Built-in middleware
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    r.Use(middleware.RequestID)

    // Route grouping
    r.Route("/api/v1", func(r chi.Router) {
        r.Route("/users", func(r chi.Router) {
            r.Get("/", listUsers)
            r.Post("/", createUser)

            r.Route("/{userID}", func(r chi.Router) {
                r.Use(UserContext) // Middleware for nested routes
                r.Get("/", getUser)
                r.Put("/", updateUser)
                r.Delete("/", deleteUser)
            })
        })
    })

    http.ListenAndServe(":8080", r)
}

func UserContext(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        userID := chi.URLParam(r, "userID")
        // Load user from database, set in context
        ctx := context.WithValue(r.Context(), "user", userID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func getUser(w http.ResponseWriter, r *http.Request) {
    userID := r.Context().Value("user").(string)
    // Return user data
    json.NewEncoder(w).Encode(map[string]string{"id": userID})
}

Gin - High Performance Framework

Gin - 高性能框架

Use When:
  • Need maximum performance (8x faster than most frameworks)
  • Want batteries-included experience
  • Require built-in validation and binding
  • Building JSON APIs with minimal boilerplate
Strengths:
  • Extremely fast (fastest Go framework in benchmarks)
  • Built-in JSON binding and validation
  • Middleware ecosystem
  • Group-based routing
  • Custom error handling
Installation:
bash
go get -u github.com/gin-gonic/gin
Example:
go
package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

type CreateUserRequest struct {
    Name  string `json:"name" binding:"required,min=3"`
    Email string `json:"email" binding:"required,email"`
    Age   int    `json:"age" binding:"required,gte=18"`
}

func main() {
    r := gin.Default() // Logger + Recovery middleware

    api := r.Group("/api/v1")
    {
        users := api.Group("/users")
        {
            users.GET("", listUsers)
            users.POST("", createUser)
            users.GET("/:id", getUser)
            users.PUT("/:id", updateUser)
            users.DELETE("/:id", deleteUser)
        }
    }

    r.Run(":8080")
}

func createUser(c *gin.Context) {
    var req CreateUserRequest

    // Automatic validation based on struct tags
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    // Process user creation
    user := User{
        ID:    generateID(),
        Name:  req.Name,
        Email: req.Email,
        Age:   req.Age,
    }

    c.JSON(http.StatusCreated, user)
}

func getUser(c *gin.Context) {
    id := c.Param("id")

    // Return user
    c.JSON(http.StatusOK, gin.H{
        "id":   id,
        "name": "John Doe",
    })
}
适用场景:
  • 需要极致性能(比大多数框架快8倍)
  • 想要开箱即用的体验
  • 需要内置验证和绑定功能
  • 以最少样板代码构建JSON API
优势:
  • 极快的速度(基准测试中最快的Go框架)
  • 内置JSON绑定和验证
  • 丰富的中间件生态系统
  • 基于分组的路由
  • 自定义错误处理
安装:
bash
go get -u github.com/gin-gonic/gin
示例:
go
package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

type CreateUserRequest struct {
    Name  string `json:"name" binding:"required,min=3"`
    Email string `json:"email" binding:"required,email"`
    Age   int    `json:"age" binding:"required,gte=18"`
}

func main() {
    r := gin.Default() // Logger + Recovery middleware

    api := r.Group("/api/v1")
    {
        users := api.Group("/users")
        {
            users.GET("", listUsers)
            users.POST("", createUser)
            users.GET("/:id", getUser)
            users.PUT("/:id", updateUser)
            users.DELETE("/:id", deleteUser)
        }
    }

    r.Run(":8080")
}

func createUser(c *gin.Context) {
    var req CreateUserRequest

    // Automatic validation based on struct tags
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    // Process user creation
    user := User{
        ID:    generateID(),
        Name:  req.Name,
        Email: req.Email,
        Age:   req.Age,
    }

    c.JSON(http.StatusCreated, user)
}

func getUser(c *gin.Context) {
    id := c.Param("id")

    // Return user
    c.JSON(http.StatusOK, gin.H{
        "id":   id,
        "name": "John Doe",
    })
}

Echo - Enterprise Framework

Echo - 企业级框架

Use When:
  • Building enterprise applications
  • Need OpenAPI/Swagger integration
  • Want comprehensive middleware library
  • Require type-safe routing and binding
Strengths:
  • Type-safe routing with automatic parameter binding
  • Built-in middleware for common patterns
  • OpenAPI/Swagger generation support
  • Excellent error handling middleware
  • WebSocket support
Installation:
bash
go get -u github.com/labstack/echo/v4
Example:
go
package main

import (
    "net/http"
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func main() {
    e := echo.New()

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(middleware.CORS())

    // Routes
    e.GET("/users/:id", getUser)
    e.POST("/users", createUser)
    e.PUT("/users/:id", updateUser)
    e.DELETE("/users/:id", deleteUser)

    e.Logger.Fatal(e.Start(":8080"))
}

func getUser(c echo.Context) error {
    id := c.Param("id")

    user := User{ID: id, Name: "John Doe"}
    return c.JSON(http.StatusOK, user)
}

func createUser(c echo.Context) error {
    var user User

    if err := c.Bind(&user); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    if err := c.Validate(&user); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    return c.JSON(http.StatusCreated, user)
}

// Custom error handler
func customErrorHandler(err error, c echo.Context) {
    code := http.StatusInternalServerError
    message := "Internal Server Error"

    if he, ok := err.(*echo.HTTPError); ok {
        code = he.Code
        message = he.Message.(string)
    }

    c.JSON(code, map[string]string{"error": message})
}
适用场景:
  • 构建企业级应用
  • 需要OpenAPI/Swagger集成
  • 想要全面的中间件库
  • 需要类型安全的路由和绑定
优势:
  • 类型安全的路由,支持自动参数绑定
  • 内置常见模式的中间件
  • 支持OpenAPI/Swagger生成
  • 优秀的错误处理中间件
  • 支持WebSocket
安装:
bash
go get -u github.com/labstack/echo/v4
示例:
go
package main

import (
    "net/http"
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func main() {
    e := echo.New()

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(middleware.CORS())

    // Routes
    e.GET("/users/:id", getUser)
    e.POST("/users", createUser)
    e.PUT("/users/:id", updateUser)
    e.DELETE("/users/:id", deleteUser)

    e.Logger.Fatal(e.Start(":8080"))
}

func getUser(c echo.Context) error {
    id := c.Param("id")

    user := User{ID: id, Name: "John Doe"}
    return c.JSON(http.StatusOK, user)
}

func createUser(c echo.Context) error {
    var user User

    if err := c.Bind(&user); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    if err := c.Validate(&user); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    return c.JSON(http.StatusCreated, user)
}

// Custom error handler
func customErrorHandler(err error, c echo.Context) {
    code := http.StatusInternalServerError
    message := "Internal Server Error"

    if he, ok := err.(*echo.HTTPError); ok {
        code = he.Code
        message = he.Message.(string)
    }

    c.JSON(code, map[string]string{"error": message})
}

Fiber - Express.js Style

Fiber - Express.js风格

Use When:
  • Team familiar with Express.js patterns
  • Need WebSocket support out of the box
  • Building real-time applications
  • Want fastest route matching performance
Strengths:
  • Express.js-inspired API (easy for Node.js developers)
  • Fastest route matching (uses fasthttp)
  • Built-in WebSocket support
  • Template engine support
  • File upload handling
Limitations:
  • Uses fasthttp instead of net/http (less ecosystem compatibility)
  • Not compatible with standard http.Handler interface
  • Slightly less mature ecosystem
Installation:
bash
go get -u github.com/gofiber/fiber/v2
Example:
go
package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/logger"
    "github.com/gofiber/fiber/v2/middleware/cors"
)

func main() {
    app := fiber.New()

    // Middleware
    app.Use(logger.New())
    app.Use(cors.New())

    // Routes
    api := app.Group("/api/v1")

    users := api.Group("/users")
    users.Get("/", listUsers)
    users.Post("/", createUser)
    users.Get("/:id", getUser)
    users.Put("/:id", updateUser)
    users.Delete("/:id", deleteUser)

    app.Listen(":8080")
}

func getUser(c *fiber.Ctx) error {
    id := c.Params("id")

    return c.JSON(fiber.Map{
        "id":   id,
        "name": "John Doe",
    })
}

func createUser(c *fiber.Ctx) error {
    var user User

    if err := c.BodyParser(&user); err != nil {
        return c.Status(400).JSON(fiber.Map{"error": err.Error()})
    }

    return c.Status(201).JSON(user)
}
适用场景:
  • 团队熟悉Express.js模式
  • 需要开箱即用的WebSocket支持
  • 构建实时应用
  • 想要最快的路由匹配性能
优势:
  • 受Express.js启发的API(对Node.js开发者友好)
  • 最快的路由匹配(使用fasthttp)
  • 内置WebSocket支持
  • 支持模板引擎
  • 处理文件上传
局限性:
  • 使用fasthttp而非net/http(生态系统兼容性较差)
  • 不兼容标准http.Handler接口
  • 生态系统稍不成熟
安装:
bash
go get -u github.com/gofiber/fiber/v2
示例:
go
package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/logger"
    "github.com/gofiber/fiber/v2/middleware/cors"
)

func main() {
    app := fiber.New()

    // Middleware
    app.Use(logger.New())
    app.Use(cors.New())

    // Routes
    api := app.Group("/api/v1")

    users := api.Group("/users")
    users.Get("/", listUsers)
    users.Post("/", createUser)
    users.Get("/:id", getUser)
    users.Put("/:id", updateUser)
    users.Delete("/:id", deleteUser)

    app.Listen(":8080")
}

func getUser(c *fiber.Ctx) error {
    id := c.Params("id")

    return c.JSON(fiber.Map{
        "id":   id,
        "name": "John Doe",
    })
}

func createUser(c *fiber.Ctx) error {
    var user User

    if err := c.BodyParser(&user); err != nil {
        return c.Status(400).JSON(fiber.Map{"error": err.Error()})
    }

    return c.Status(201).JSON(user)
}

Common HTTP Patterns

常见HTTP模式

Request Validation

请求验证

Struct Tag Validation:
go
import "github.com/go-playground/validator/v10"

type CreateUserRequest struct {
    Name     string `json:"name" validate:"required,min=3,max=50"`
    Email    string `json:"email" validate:"required,email"`
    Age      int    `json:"age" validate:"required,gte=18,lte=120"`
    Password string `json:"password" validate:"required,min=8"`
}

var validate = validator.New()

func validateRequest(req interface{}) error {
    return validate.Struct(req)
}

// Usage
func createUser(w http.ResponseWriter, r *http.Request) {
    var req CreateUserRequest

    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    if err := validateRequest(&req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Process valid request
}
Custom Validators:
go
import "github.com/go-playground/validator/v10"

var validate = validator.New()

func init() {
    validate.RegisterValidation("username", validateUsername)
}

func validateUsername(fl validator.FieldLevel) bool {
    username := fl.Field().String()
    // Custom validation logic
    return len(username) >= 3 && isAlphanumeric(username)
}

type SignupRequest struct {
    Username string `validate:"required,username"`
    Email    string `validate:"required,email"`
}
结构体标签验证:
go
import "github.com/go-playground/validator/v10"

type CreateUserRequest struct {
    Name     string `json:"name" validate:"required,min=3,max=50"`
    Email    string `json:"email" validate:"required,email"`
    Age      int    `json:"age" validate:"required,gte=18,lte=120"`
    Password string `json:"password" validate:"required,min=8"`
}

var validate = validator.New()

func validateRequest(req interface{}) error {
    return validate.Struct(req)
}

// Usage
func createUser(w http.ResponseWriter, r *http.Request) {
    var req CreateUserRequest

    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    if err := validateRequest(&req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Process valid request
}
自定义验证器:
go
import "github.com/go-playground/validator/v10"

var validate = validator.New()

func init() {
    validate.RegisterValidation("username", validateUsername)
}

func validateUsername(fl validator.FieldLevel) bool {
    username := fl.Field().String()
    // Custom validation logic
    return len(username) >= 3 && isAlphanumeric(username)
}

type SignupRequest struct {
    Username string `validate:"required,username"`
    Email    string `validate:"required,email"`
}

Middleware Patterns

中间件模式

Authentication Middleware:
go
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")

        if token == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        // Validate token
        userID, err := validateToken(token)
        if err != nil {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }

        // Add user to context
        ctx := context.WithValue(r.Context(), "userID", userID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}
Rate Limiting Middleware:
go
import (
    "golang.org/x/time/rate"
    "sync"
)

type RateLimiter struct {
    limiters map[string]*rate.Limiter
    mu       sync.RWMutex
    rate     rate.Limit
    burst    int
}

func NewRateLimiter(r rate.Limit, b int) *RateLimiter {
    return &RateLimiter{
        limiters: make(map[string]*rate.Limiter),
        rate:     r,
        burst:    b,
    }
}

func (rl *RateLimiter) getLimiter(ip string) *rate.Limiter {
    rl.mu.Lock()
    defer rl.mu.Unlock()

    limiter, exists := rl.limiters[ip]
    if !exists {
        limiter = rate.NewLimiter(rl.rate, rl.burst)
        rl.limiters[ip] = limiter
    }

    return limiter
}

func (rl *RateLimiter) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        limiter := rl.getLimiter(ip)

        if !limiter.Allow() {
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }

        next.ServeHTTP(w, r)
    })
}
CORS Middleware:
go
func CORSMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }

        next.ServeHTTP(w, r)
    })
}
认证中间件:
go
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")

        if token == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        // Validate token
        userID, err := validateToken(token)
        if err != nil {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }

        // Add user to context
        ctx := context.WithValue(r.Context(), "userID", userID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}
限流中间件:
go
import (
    "golang.org/x/time/rate"
    "sync"
)

type RateLimiter struct {
    limiters map[string]*rate.Limiter
    mu       sync.RWMutex
    rate     rate.Limit
    burst    int
}

func NewRateLimiter(r rate.Limit, b int) *RateLimiter {
    return &RateLimiter{
        limiters: make(map[string]*rate.Limiter),
        rate:     r,
        burst:    b,
    }
}

func (rl *RateLimiter) getLimiter(ip string) *rate.Limiter {
    rl.mu.Lock()
    defer rl.mu.Unlock()

    limiter, exists := rl.limiters[ip]
    if !exists {
        limiter = rate.NewLimiter(rl.rate, rl.burst)
        rl.limiters[ip] = limiter
    }

    return limiter
}

func (rl *RateLimiter) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        limiter := rl.getLimiter(ip)

        if !limiter.Allow() {
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }

        next.ServeHTTP(w, r)
    })
}
CORS中间件:
go
func CORSMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }

        next.ServeHTTP(w, r)
    })
}

Error Handling Strategies

错误处理策略

Custom Error Types:
go
type APIError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Details string `json:"details,omitempty"`
}

func (e *APIError) Error() string {
    return e.Message
}

// Error constructors
func NewBadRequestError(msg string) *APIError {
    return &APIError{Code: http.StatusBadRequest, Message: msg}
}

func NewNotFoundError(msg string) *APIError {
    return &APIError{Code: http.StatusNotFound, Message: msg}
}

func NewInternalError(msg string) *APIError {
    return &APIError{Code: http.StatusInternalServerError, Message: msg}
}
Error Response Middleware:
go
type APIHandler func(w http.ResponseWriter, r *http.Request) error

func ErrorHandlerMiddleware(h APIHandler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        err := h(w, r)
        if err == nil {
            return
        }

        // Handle different error types
        var apiErr *APIError
        if errors.As(err, &apiErr) {
            w.Header().Set("Content-Type", "application/json")
            w.WriteHeader(apiErr.Code)
            json.NewEncoder(w).Encode(apiErr)
            return
        }

        // Unknown error - log and return generic message
        log.Printf("Internal error: %v", err)
        w.WriteHeader(http.StatusInternalServerError)
        json.NewEncoder(w).Encode(APIError{
            Code:    http.StatusInternalServerError,
            Message: "Internal server error",
        })
    })
}

// Usage
func getUserHandler(w http.ResponseWriter, r *http.Request) error {
    id := r.PathValue("id")

    user, err := db.GetUser(id)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return NewNotFoundError("User not found")
        }
        return fmt.Errorf("database error: %w", err)
    }

    w.Header().Set("Content-Type", "application/json")
    return json.NewEncoder(w).Encode(user)
}

// Register with middleware
mux.Handle("GET /users/{id}", ErrorHandlerMiddleware(getUserHandler))
自定义错误类型:
go
type APIError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Details string `json:"details,omitempty"`
}

func (e *APIError) Error() string {
    return e.Message
}

// Error constructors
func NewBadRequestError(msg string) *APIError {
    return &APIError{Code: http.StatusBadRequest, Message: msg}
}

func NewNotFoundError(msg string) *APIError {
    return &APIError{Code: http.StatusNotFound, Message: msg}
}

func NewInternalError(msg string) *APIError {
    return &APIError{Code: http.StatusInternalServerError, Message: msg}
}
错误响应中间件:
go
type APIHandler func(w http.ResponseWriter, r *http.Request) error

func ErrorHandlerMiddleware(h APIHandler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        err := h(w, r)
        if err == nil {
            return
        }

        // Handle different error types
        var apiErr *APIError
        if errors.As(err, &apiErr) {
            w.Header().Set("Content-Type", "application/json")
            w.WriteHeader(apiErr.Code)
            json.NewEncoder(w).Encode(apiErr)
            return
        }

        // Unknown error - log and return generic message
        log.Printf("Internal error: %v", err)
        w.WriteHeader(http.StatusInternalServerError)
        json.NewEncoder(w).Encode(APIError{
            Code:    http.StatusInternalServerError,
            Message: "Internal server error",
        })
    })
}

// Usage
func getUserHandler(w http.ResponseWriter, r *http.Request) error {
    id := r.PathValue("id")

    user, err := db.GetUser(id)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return NewNotFoundError("User not found")
        }
        return fmt.Errorf("database error: %w", err)
    }

    w.Header().Set("Content-Type", "application/json")
    return json.NewEncoder(w).Encode(user)
}

// Register with middleware
mux.Handle("GET /users/{id}", ErrorHandlerMiddleware(getUserHandler))

REST API Design Patterns

REST API设计模式

Resource Naming Conventions:
go
// Good: Plural nouns for collections
GET    /api/v1/users           // List users
POST   /api/v1/users           // Create user
GET    /api/v1/users/{id}      // Get user
PUT    /api/v1/users/{id}      // Update user (full)
PATCH  /api/v1/users/{id}      // Update user (partial)
DELETE /api/v1/users/{id}      // Delete user

// Nested resources
GET    /api/v1/users/{id}/posts        // User's posts
POST   /api/v1/users/{id}/posts        // Create post for user
GET    /api/v1/users/{id}/posts/{pid}  // Specific post

// Avoid: Verbs in URLs (use HTTP methods instead)
// Bad: POST /api/v1/users/create
// Bad: GET  /api/v1/users/get/{id}
Pagination Pattern:
go
type PaginationParams struct {
    Page     int `json:"page" validate:"gte=1"`
    PageSize int `json:"page_size" validate:"gte=1,lte=100"`
}

type PaginatedResponse struct {
    Data       interface{} `json:"data"`
    Page       int         `json:"page"`
    PageSize   int         `json:"page_size"`
    TotalCount int         `json:"total_count"`
    TotalPages int         `json:"total_pages"`
}

func listUsers(w http.ResponseWriter, r *http.Request) {
    // Parse pagination params
    page, _ := strconv.Atoi(r.URL.Query().Get("page"))
    if page < 1 {
        page = 1
    }

    pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
    if pageSize < 1 || pageSize > 100 {
        pageSize = 20
    }

    // Fetch data
    users, totalCount := db.GetUsers(page, pageSize)
    totalPages := (totalCount + pageSize - 1) / pageSize

    response := PaginatedResponse{
        Data:       users,
        Page:       page,
        PageSize:   pageSize,
        TotalCount: totalCount,
        TotalPages: totalPages,
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}
Query Parameter Filtering:
go
type UserFilter struct {
    Status    string `json:"status"`
    Role      string `json:"role"`
    CreatedAt string `json:"created_at"`
    Search    string `json:"search"`
}

func parseFilters(r *http.Request) UserFilter {
    return UserFilter{
        Status:    r.URL.Query().Get("status"),
        Role:      r.URL.Query().Get("role"),
        CreatedAt: r.URL.Query().Get("created_at"),
        Search:    r.URL.Query().Get("search"),
    }
}

func listUsers(w http.ResponseWriter, r *http.Request) {
    filters := parseFilters(r)

    users := db.GetUsersWithFilters(filters)

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(users)
}

// Example request: GET /api/v1/users?status=active&role=admin&search=john
资源命名规范:
go
// 推荐:集合使用复数名词
GET    /api/v1/users           // 列出用户
POST   /api/v1/users           // 创建用户
GET    /api/v1/users/{id}      // 获取用户
PUT    /api/v1/users/{id}      // 全量更新用户
PATCH  /api/v1/users/{id}      // 部分更新用户
DELETE /api/v1/users/{id}      // 删除用户

// 嵌套资源
GET    /api/v1/users/{id}/posts        // 用户的文章
POST   /api/v1/users/{id}/posts        // 为用户创建文章
GET    /api/v1/users/{id}/posts/{pid}  // 获取指定文章

// 避免:URL中使用动词(改用HTTP方法)
// 不推荐:POST /api/v1/users/create
// 不推荐:GET  /api/v1/users/get/{id}
分页模式:
go
type PaginationParams struct {
    Page     int `json:"page" validate:"gte=1"`
    PageSize int `json:"page_size" validate:"gte=1,lte=100"`
}

type PaginatedResponse struct {
    Data       interface{} `json:"data"`
    Page       int         `json:"page"`
    PageSize   int         `json:"page_size"`
    TotalCount int         `json:"total_count"`
    TotalPages int         `json:"total_pages"`
}

func listUsers(w http.ResponseWriter, r *http.Request) {
    // 解析分页参数
    page, _ := strconv.Atoi(r.URL.Query().Get("page"))
    if page < 1 {
        page = 1
    }

    pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
    if pageSize < 1 || pageSize > 100 {
        pageSize = 20
    }

    // 获取数据
    users, totalCount := db.GetUsers(page, pageSize)
    totalPages := (totalCount + pageSize - 1) / pageSize

    response := PaginatedResponse{
        Data:       users,
        Page:       page,
        PageSize:   pageSize,
        TotalCount: totalCount,
        TotalPages: totalPages,
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}
查询参数过滤:
go
type UserFilter struct {
    Status    string `json:"status"`
    Role      string `json:"role"`
    CreatedAt string `json:"created_at"`
    Search    string `json:"search"`
}

func parseFilters(r *http.Request) UserFilter {
    return UserFilter{
        Status:    r.URL.Query().Get("status"),
        Role:      r.URL.Query().Get("role"),
        CreatedAt: r.URL.Query().Get("created_at"),
        Search:    r.URL.Query().Get("search"),
    }
}

func listUsers(w http.ResponseWriter, r *http.Request) {
    filters := parseFilters(r)

    users := db.GetUsersWithFilters(filters)

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(users)
}

// 请求示例:GET /api/v1/users?status=active&role=admin&search=john

HTTP Client Patterns

HTTP客户端模式

Production-Ready HTTP Client:
go
import (
    "context"
    "net/http"
    "time"
)

func NewHTTPClient() *http.Client {
    return &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            MaxIdleConns:        100,
            MaxIdleConnsPerHost: 10,
            IdleConnTimeout:     90 * time.Second,
            DisableKeepAlives:   false,
        },
    }
}

// Making requests with context
func fetchUser(ctx context.Context, userID string) (*User, error) {
    client := NewHTTPClient()

    req, err := http.NewRequestWithContext(
        ctx,
        "GET",
        fmt.Sprintf("https://api.example.com/users/%s", userID),
        nil,
    )
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }

    req.Header.Set("Accept", "application/json")
    req.Header.Set("Authorization", "Bearer "+getToken())

    resp, err := client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("execute request: %w", err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
    }

    var user User
    if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }

    return &user, nil
}
Retry Logic with Exponential Backoff:
go
import (
    "context"
    "math"
    "time"
)

type RetryConfig struct {
    MaxRetries int
    BaseDelay  time.Duration
    MaxDelay   time.Duration
}

func DoWithRetry(ctx context.Context, cfg RetryConfig, fn func() error) error {
    var err error

    for attempt := 0; attempt <= cfg.MaxRetries; attempt++ {
        err = fn()
        if err == nil {
            return nil
        }

        if attempt < cfg.MaxRetries {
            // Exponential backoff
            delay := time.Duration(math.Pow(2, float64(attempt))) * cfg.BaseDelay
            if delay > cfg.MaxDelay {
                delay = cfg.MaxDelay
            }

            select {
            case <-ctx.Done():
                return ctx.Err()
            case <-time.After(delay):
                // Continue to next attempt
            }
        }
    }

    return fmt.Errorf("max retries exceeded: %w", err)
}

// Usage
err := DoWithRetry(ctx, RetryConfig{
    MaxRetries: 3,
    BaseDelay:  100 * time.Millisecond,
    MaxDelay:   2 * time.Second,
}, func() error {
    return makeAPIRequest()
})
生产级HTTP客户端:
go
import (
    "context"
    "net/http"
    "time"
)

func NewHTTPClient() *http.Client {
    return &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            MaxIdleConns:        100,
            MaxIdleConnsPerHost: 10,
            IdleConnTimeout:     90 * time.Second,
            DisableKeepAlives:   false,
        },
    }
}

// 使用上下文发起请求
func fetchUser(ctx context.Context, userID string) (*User, error) {
    client := NewHTTPClient()

    req, err := http.NewRequestWithContext(
        ctx,
        "GET",
        fmt.Sprintf("https://api.example.com/users/%s", userID),
        nil,
    )
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }

    req.Header.Set("Accept", "application/json")
    req.Header.Set("Authorization", "Bearer "+getToken())

    resp, err := client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("execute request: %w", err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
    }

    var user User
    if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }

    return &user, nil
}
带指数退避的重试逻辑:
go
import (
    "context"
    "math"
    "time"
)

type RetryConfig struct {
    MaxRetries int
    BaseDelay  time.Duration
    MaxDelay   time.Duration
}

func DoWithRetry(ctx context.Context, cfg RetryConfig, fn func() error) error {
    var err error

    for attempt := 0; attempt <= cfg.MaxRetries; attempt++ {
        err = fn()
        if err == nil {
            return nil
        }

        if attempt < cfg.MaxRetries {
            // 指数退避
            delay := time.Duration(math.Pow(2, float64(attempt))) * cfg.BaseDelay
            if delay > cfg.MaxDelay {
                delay = cfg.MaxDelay
            }

            select {
            case <-ctx.Done():
                return ctx.Err()
            case <-time.After(delay):
                // 继续下一次尝试
            }
        }
    }

    return fmt.Errorf("max retries exceeded: %w", err)
}

// 使用示例
err := DoWithRetry(ctx, RetryConfig{
    MaxRetries: 3,
    BaseDelay:  100 * time.Millisecond,
    MaxDelay:   2 * time.Second,
}, func() error {
    return makeAPIRequest()
})

Testing HTTP Handlers

测试HTTP处理器

Using httptest.Server:
go
import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestGetUser(t *testing.T) {
    // Create test server
    ts := httptest.NewServer(http.HandlerFunc(getUserHandler))
    defer ts.Close()

    // Make request
    resp, err := http.Get(ts.URL + "/users/123")
    if err != nil {
        t.Fatal(err)
    }
    defer resp.Body.Close()

    // Assertions
    if resp.StatusCode != http.StatusOK {
        t.Errorf("expected status 200, got %d", resp.StatusCode)
    }

    var user User
    if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
        t.Fatal(err)
    }

    if user.ID != "123" {
        t.Errorf("expected user ID 123, got %s", user.ID)
    }
}
Testing Middleware:
go
func TestAuthMiddleware(t *testing.T) {
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        userID := r.Context().Value("userID")
        if userID == nil {
            t.Error("userID not found in context")
        }
        w.WriteHeader(http.StatusOK)
    })

    wrapped := AuthMiddleware(handler)

    tests := []struct {
        name       string
        token      string
        wantStatus int
    }{
        {"Valid token", "valid-token-123", http.StatusOK},
        {"Missing token", "", http.StatusUnauthorized},
        {"Invalid token", "invalid", http.StatusUnauthorized},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            req := httptest.NewRequest("GET", "/test", nil)
            if tt.token != "" {
                req.Header.Set("Authorization", tt.token)
            }

            rr := httptest.NewRecorder()
            wrapped.ServeHTTP(rr, req)

            if rr.Code != tt.wantStatus {
                t.Errorf("expected status %d, got %d", tt.wantStatus, rr.Code)
            }
        })
    }
}
使用httptest.Server:
go
import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestGetUser(t *testing.T) {
    // 创建测试服务器
    ts := httptest.NewServer(http.HandlerFunc(getUserHandler))
    defer ts.Close()

    // 发起请求
    resp, err := http.Get(ts.URL + "/users/123")
    if err != nil {
        t.Fatal(err)
    }
    defer resp.Body.Close()

    // 断言
    if resp.StatusCode != http.StatusOK {
        t.Errorf("expected status 200, got %d", resp.StatusCode)
    }

    var user User
    if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
        t.Fatal(err)
    }

    if user.ID != "123" {
        t.Errorf("expected user ID 123, got %s", user.ID)
    }
}
测试中间件:
go
func TestAuthMiddleware(t *testing.T) {
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        userID := r.Context().Value("userID")
        if userID == nil {
            t.Error("userID not found in context")
        }
        w.WriteHeader(http.StatusOK)
    })

    wrapped := AuthMiddleware(handler)

    tests := []struct {
        name       string
        token      string
        wantStatus int
    }{
        {"Valid token", "valid-token-123", http.StatusOK},
        {"Missing token", "", http.StatusUnauthorized},
        {"Invalid token", "invalid", http.StatusUnauthorized},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            req := httptest.NewRequest("GET", "/test", nil)
            if tt.token != "" {
                req.Header.Set("Authorization", tt.token)
            }

            rr := httptest.NewRecorder()
            wrapped.ServeHTTP(rr, req)

            if rr.Code != tt.wantStatus {
                t.Errorf("expected status %d, got %d", tt.wantStatus, rr.Code)
            }
        })
    }
}

Performance Optimization

性能优化

Response Compression:
go
import (
    "compress/gzip"
    "io"
    "net/http"
    "strings"
)

type gzipResponseWriter struct {
    io.Writer
    http.ResponseWriter
}

func (w gzipResponseWriter) Write(b []byte) (int, error) {
    return w.Writer.Write(b)
}

func GzipMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
            next.ServeHTTP(w, r)
            return
        }

        w.Header().Set("Content-Encoding", "gzip")
        gz := gzip.NewWriter(w)
        defer gz.Close()

        gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
        next.ServeHTTP(gzw, r)
    })
}
Connection Pooling Configuration:
go
import (
    "net"
    "net/http"
    "time"
)

func NewProductionHTTPClient() *http.Client {
    return &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            Proxy: http.ProxyFromEnvironment,
            DialContext: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
            }).DialContext,
            ForceAttemptHTTP2:     true,
            MaxIdleConns:          100,
            MaxIdleConnsPerHost:   10,
            IdleConnTimeout:       90 * time.Second,
            TLSHandshakeTimeout:   10 * time.Second,
            ExpectContinueTimeout: 1 * time.Second,
        },
    }
}
响应压缩:
go
import (
    "compress/gzip"
    "io"
    "net/http"
    "strings"
)

type gzipResponseWriter struct {
    io.Writer
    http.ResponseWriter
}

func (w gzipResponseWriter) Write(b []byte) (int, error) {
    return w.Writer.Write(b)
}

func GzipMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
            next.ServeHTTP(w, r)
            return
        }

        w.Header().Set("Content-Encoding", "gzip")
        gz := gzip.NewWriter(w)
        defer gz.Close()

        gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
        next.ServeHTTP(gzw, r)
    })
}
连接池配置:
go
import (
    "net"
    "net/http"
    "time"
)

func NewProductionHTTPClient() *http.Client {
    return &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            Proxy: http.ProxyFromEnvironment,
            DialContext: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
            }).DialContext,
            ForceAttemptHTTP2:     true,
            MaxIdleConns:          100,
            MaxIdleConnsPerHost:   10,
            IdleConnTimeout:       90 * time.Second,
            TLSHandshakeTimeout:   10 * time.Second,
            ExpectContinueTimeout: 1 * time.Second,
        },
    }
}

Decision Tree

决策树

Building HTTP API in Go?
├─ Simple API, few dependencies? → net/http (stdlib)
│  ├─ Go 1.22+? → Use new ServeMux pattern routing
│  └─ Go < 1.22? → Consider Chi for better routing
├─ Need stdlib compatibility + better ergonomics? → Chi
│  └─ Great for: Middleware chains, route grouping
├─ Maximum performance priority? → Gin
│  └─ Great for: JSON APIs, high throughput services
├─ Enterprise app with OpenAPI? → Echo
│  └─ Great for: Type safety, comprehensive middleware
└─ Team knows Express.js + need WebSockets? → Fiber
   └─ Note: Uses fasthttp, not stdlib-compatible
要在Go中构建HTTP API?
├─ 简单API,依赖少? → net/http(标准库)
│  ├─ 使用Go 1.22+? → 使用新的ServeMux模式路由
│  └─ 使用Go < 1.22? → 考虑使用Chi获得更好的路由能力
├─ 需要兼容标准库且更易用? → Chi
│  └─ 适用场景:中间件链、路由分组
├─ 优先考虑极致性能? → Gin
│  └─ 适用场景:JSON API、高吞吐量服务
├─ 企业级应用且需要OpenAPI? → Echo
│  └─ 适用场景:类型安全、全面的中间件
└─ 团队熟悉Express.js且需要WebSocket? → Fiber
   └─ 注意:使用fasthttp,不兼容标准库

Common Pitfalls

常见陷阱

Pitfall 1: Not Closing Response Bodies
go
// Bad: Memory leak
resp, _ := http.Get(url)
body, _ := io.ReadAll(resp.Body) // Never closed!

// Good: Always defer close
resp, err := http.Get(url)
if err != nil {
    return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
Pitfall 2: Not Using Context for Timeouts
go
// Bad: No timeout control
resp, _ := http.Get(url)

// Good: Use context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := client.Do(req)
Pitfall 3: Ignoring HTTP Status Codes
go
// Bad: Not checking status
resp, _ := http.Get(url)
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&result)

// Good: Always check status
resp, err := http.Get(url)
if err != nil {
    return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
    return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
Pitfall 4: Not Reusing HTTP Clients
go
// Bad: Creates new client per request (no connection pooling)
func makeRequest() {
    client := &http.Client{}
    resp, _ := client.Get(url)
}

// Good: Reuse client for connection pooling
var httpClient = &http.Client{
    Timeout: 30 * time.Second,
}

func makeRequest() {
    resp, _ := httpClient.Get(url)
}
陷阱1:未关闭响应体
go
// 错误:内存泄漏
resp, _ := http.Get(url)
body, _ := io.ReadAll(resp.Body) // 从未关闭!

// 正确:始终延迟关闭
resp, err := http.Get(url)
if err != nil {
    return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
陷阱2:未使用上下文设置超时
go
// 错误:无超时控制
resp, _ := http.Get(url)

// 正确:使用带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := client.Do(req)
陷阱3:忽略HTTP状态码
go
// 错误:未检查状态码
resp, _ := http.Get(url)
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&result)

// 正确:始终检查状态码
resp, err := http.Get(url)
if err != nil {
    return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
    return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
陷阱4:未复用HTTP客户端
go
// 错误:每次请求创建新客户端(无连接池)
func makeRequest() {
    client := &http.Client{}
    resp, _ := client.Get(url)
}

// 正确:复用客户端以实现连接池
var httpClient = &http.Client{
    Timeout: 30 * time.Second,
}

func makeRequest() {
    resp, _ := httpClient.Get(url)
}

Related Skills

相关技能

  • golang-testing-strategies: Testing HTTP handlers and middleware
  • golang-database-patterns: Integrating databases with HTTP APIs
  • toolchains-typescript-frameworks-nodejs-backend: Comparison with Node.js patterns
  • golang-testing-strategies:测试HTTP处理器和中间件
  • golang-database-patterns:将数据库与HTTP API集成
  • toolchains-typescript-frameworks-nodejs-backend:与Node.js模式对比

References

参考资料