1. Go Web 框架对比

Gin

~140k req/s

最流行,生态最完善,文档丰富。适合大多数业务服务。

Echo

~120k req/s

API 设计优雅,内置 Validator,适合 API 服务开发。

Fiber

~300k req/s

基于 fasthttp,性能极致,Express 风格 API,适合高并发场景。

特性GinEchoFibernet/http
性能优秀优秀极佳良好
生态最丰富丰富增长中标准库
学习曲线很低(类Express)
内存兼容标准库兼容标准库兼容⚠️ fasthttp不兼容标准
推荐场景通用业务API服务高并发/低延迟工具/简单服务

2. Gin 框架实战

安装与基础路由

go get github.com/gin-gonic/gin
package main

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

func main() {
    // gin.Default() = gin.New() + Logger + Recovery 中间件
    r := gin.Default()

    // ─── 路由定义 ──────────────────────────────────────────
    // 基础路由
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "pong"})
    })

    // 路径参数
    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.JSON(200, gin.H{"user_id": id})
    })

    // 查询参数: /search?q=go&page=1
    r.GET("/search", func(c *gin.Context) {
        q := c.Query("q")
        page := c.DefaultQuery("page", "1")
        c.JSON(200, gin.H{"query": q, "page": page})
    })

    // 路由组 (便于统一前缀和中间件)
    api := r.Group("/api/v1")
    {
        api.GET("/users", listUsers)
        api.POST("/users", createUser)
        api.GET("/users/:id", getUser)
        api.PUT("/users/:id", updateUser)
        api.DELETE("/users/:id", deleteUser)
    }

    r.Run(":8080")
}

请求绑定与校验

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
)

// 请求结构体 (binding tag 驱动校验)
type CreateUserRequest struct {
    Name     string `json:"name"  binding:"required,min=2,max=50"`
    Email    string `json:"email" binding:"required,email"`
    Password string `json:"password" binding:"required,min=8"`
    Age      int    `json:"age"   binding:"omitempty,min=0,max=150"`
    Role     string `json:"role"  binding:"oneof=admin user guest"`
}

type UpdateUserRequest struct {
    Name  *string `json:"name"  binding:"omitempty,min=2,max=50"`
    Email *string `json:"email" binding:"omitempty,email"`
}

func createUser(c *gin.Context) {
    var req CreateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        // 格式化校验错误
        var ve validator.ValidationErrors
        if errors.As(err, &ve) {
            errs := make([]string, len(ve))
            for i, e := range ve {
                errs[i] = fmt.Sprintf("字段 %s: %s", e.Field(), e.Tag())
            }
            c.JSON(http.StatusBadRequest, gin.H{
                "error":  "参数校验失败",
                "detail": errs,
            })
            return
        }
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    // 处理业务逻辑...
    user := User{Name: req.Name, Email: req.Email}
    c.JSON(http.StatusCreated, gin.H{
        "success": true,
        "data":    user,
    })
}

// 路径参数 + 表单绑定
type UploadRequest struct {
    Description string `form:"description"`
    Category    string `form:"category" binding:"required"`
}

func uploadFile(c *gin.Context) {
    var req UploadRequest
    if err := c.ShouldBind(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    file, err := c.FormFile("file")
    if err != nil {
        c.JSON(400, gin.H{"error": "文件不存在"})
        return
    }

    // 保存文件
    dst := "./uploads/" + file.Filename
    c.SaveUploadedFile(file, dst)
    c.JSON(200, gin.H{"filename": file.Filename, "size": file.Size})
}

中间件系统

package main

import (
    "net/http"
    "strings"
    "time"
    "github.com/gin-gonic/gin"
    "golang.org/x/time/rate"
)

// ─── 认证中间件 ────────────────────────────────────────────────
func AuthMiddleware(secretKey string) gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            token = c.Query("token")
        }
        token = strings.TrimPrefix(token, "Bearer ")

        if token == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "未提供认证 Token",
            })
            return
        }

        // 验证 JWT (简化示例)
        claims, err := validateJWT(token, secretKey)
        if err != nil {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "Token 无效或已过期",
            })
            return
        }

        // 将用户信息存入 context
        c.Set("user_id", claims.UserID)
        c.Set("user_role", claims.Role)
        c.Next()  // 继续处理
    }
}

// ─── 限流中间件 ────────────────────────────────────────────────
func RateLimitMiddleware(rps float64, burst int) gin.HandlerFunc {
    limiter := rate.NewLimiter(rate.Limit(rps), burst)
    return func(c *gin.Context) {
        if !limiter.Allow() {
            c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{
                "error":       "请求频率超限",
                "retry_after": "1s",
            })
            return
        }
        c.Next()
    }
}

// ─── 请求 ID 中间件 ────────────────────────────────────────────
func RequestIDMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        requestID := c.GetHeader("X-Request-ID")
        if requestID == "" {
            requestID = generateID() // UUID
        }
        c.Set("request_id", requestID)
        c.Header("X-Request-ID", requestID)
        c.Next()
    }
}

// ─── CORS 中间件 ───────────────────────────────────────────────
func CORSMiddleware(allowOrigins []string) gin.HandlerFunc {
    return func(c *gin.Context) {
        origin := c.GetHeader("Origin")
        allowed := false
        for _, o := range allowOrigins {
            if o == "*" || o == origin {
                allowed = true
                break
            }
        }
        if allowed {
            c.Header("Access-Control-Allow-Origin", origin)
            c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
            c.Header("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Request-ID")
            c.Header("Access-Control-Max-Age", "86400")
        }
        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }
        c.Next()
    }
}

// ─── 路由注册 ──────────────────────────────────────────────────
func setupRouter() *gin.Engine {
    r := gin.New()
    r.Use(gin.Recovery())                             // panic 恢复
    r.Use(RequestIDMiddleware())                      // 请求 ID
    r.Use(CORSMiddleware([]string{"*"}))              // CORS

    // 公开路由
    public := r.Group("/api/v1")
    public.Use(RateLimitMiddleware(100, 200))         // 100 req/s
    {
        public.POST("/auth/login", loginHandler)
        public.POST("/auth/register", registerHandler)
        public.GET("/health", healthHandler)
    }

    // 需要认证的路由
    protected := r.Group("/api/v1")
    protected.Use(AuthMiddleware("your-secret-key"))
    protected.Use(RateLimitMiddleware(50, 100))
    {
        protected.GET("/users/me", getMeHandler)
        protected.GET("/users", listUsers)
        protected.POST("/users", createUser)
    }

    return r
}

3. RESTful API 设计规范

资源命名与 URL 设计

操作方法URL状态码
获取用户列表 GET /api/v1/users 200
创建用户 POST /api/v1/users 201
获取指定用户 GET /api/v1/users/{id} 200
全量更新用户 PUT /api/v1/users/{id} 200
部分更新用户 PATCH /api/v1/users/{id} 200
删除用户 DELETE /api/v1/users/{id} 204
获取用户文章 GET /api/v1/users/{id}/posts 200

统一响应格式

package response

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

// 统一响应结构
type Response struct {
    Success   bool        `json:"success"`
    Data      any         `json:"data,omitempty"`
    Error     *ErrorInfo  `json:"error,omitempty"`
    Meta      *Meta       `json:"meta,omitempty"`
    RequestID string      `json:"request_id,omitempty"`
}

type ErrorInfo struct {
    Code    string `json:"code"`
    Message string `json:"message"`
    Detail  any    `json:"detail,omitempty"`
}

type Meta struct {
    Page       int   `json:"page"`
    PageSize   int   `json:"page_size"`
    Total      int64 `json:"total"`
    TotalPages int   `json:"total_pages"`
}

// 成功响应
func OK(c *gin.Context, data any) {
    c.JSON(http.StatusOK, Response{
        Success:   true,
        Data:      data,
        RequestID: c.GetString("request_id"),
    })
}

// 创建成功
func Created(c *gin.Context, data any) {
    c.JSON(http.StatusCreated, Response{
        Success:   true,
        Data:      data,
        RequestID: c.GetString("request_id"),
    })
}

// 分页响应
func Paginated(c *gin.Context, data any, page, pageSize int, total int64) {
    totalPages := int((total + int64(pageSize) - 1) / int64(pageSize))
    c.JSON(http.StatusOK, Response{
        Success: true,
        Data:    data,
        Meta: &Meta{
            Page:       page,
            PageSize:   pageSize,
            Total:      total,
            TotalPages: totalPages,
        },
        RequestID: c.GetString("request_id"),
    })
}

// 错误响应
func BadRequest(c *gin.Context, code, msg string, detail ...any) {
    var d any
    if len(detail) > 0 {
        d = detail[0]
    }
    c.JSON(http.StatusBadRequest, Response{
        Success: false,
        Error: &ErrorInfo{Code: code, Message: msg, Detail: d},
        RequestID: c.GetString("request_id"),
    })
}

func Unauthorized(c *gin.Context, msg string) {
    c.AbortWithStatusJSON(http.StatusUnauthorized, Response{
        Success: false,
        Error: &ErrorInfo{Code: "UNAUTHORIZED", Message: msg},
    })
}

func InternalError(c *gin.Context) {
    c.JSON(http.StatusInternalServerError, Response{
        Success: false,
        Error: &ErrorInfo{Code: "INTERNAL_ERROR", Message: "服务器内部错误"},
        RequestID: c.GetString("request_id"),
    })
}

4. 完整的用户 CRUD 示例

package handler

import (
    "strconv"
    "github.com/gin-gonic/gin"
    "myapp/internal/service"
    "myapp/internal/response"
)

type UserHandler struct {
    svc service.UserService
}

func NewUserHandler(svc service.UserService) *UserHandler {
    return &UserHandler{svc: svc}
}

// GET /api/v1/users?page=1&page_size=20&search=alice
func (h *UserHandler) List(c *gin.Context) {
    page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
    pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
    search := c.Query("search")

    if page < 1 { page = 1 }
    if pageSize < 1 || pageSize > 100 { pageSize = 20 }

    users, total, err := h.svc.ListUsers(c.Request.Context(), service.ListUsersParams{
        Page:     page,
        PageSize: pageSize,
        Search:   search,
    })
    if err != nil {
        response.InternalError(c)
        return
    }
    response.Paginated(c, users, page, pageSize, total)
}

// GET /api/v1/users/:id
func (h *UserHandler) Get(c *gin.Context) {
    id, err := strconv.ParseInt(c.Param("id"), 10, 64)
    if err != nil {
        response.BadRequest(c, "INVALID_ID", "用户 ID 格式错误")
        return
    }

    user, err := h.svc.GetUser(c.Request.Context(), id)
    if err != nil {
        if errors.Is(err, service.ErrNotFound) {
            c.JSON(404, gin.H{"error": "用户不存在"})
            return
        }
        response.InternalError(c)
        return
    }
    response.OK(c, user)
}

// POST /api/v1/users
func (h *UserHandler) Create(c *gin.Context) {
    var req service.CreateUserInput
    if err := c.ShouldBindJSON(&req); err != nil {
        response.BadRequest(c, "VALIDATION_ERROR", "参数校验失败", err.Error())
        return
    }

    user, err := h.svc.CreateUser(c.Request.Context(), req)
    if err != nil {
        if errors.Is(err, service.ErrEmailExists) {
            response.BadRequest(c, "EMAIL_EXISTS", "邮箱已被注册")
            return
        }
        response.InternalError(c)
        return
    }
    response.Created(c, user)
}

// PATCH /api/v1/users/:id
func (h *UserHandler) Update(c *gin.Context) {
    id, _ := strconv.ParseInt(c.Param("id"), 10, 64)

    // 确认只更新自己 (或管理员)
    currentUserID := c.GetInt64("user_id")
    if id != currentUserID {
        response.Unauthorized(c, "无权修改其他用户")
        return
    }

    var req service.UpdateUserInput
    if err := c.ShouldBindJSON(&req); err != nil {
        response.BadRequest(c, "VALIDATION_ERROR", "参数错误", err.Error())
        return
    }

    user, err := h.svc.UpdateUser(c.Request.Context(), id, req)
    if err != nil {
        response.InternalError(c)
        return
    }
    response.OK(c, user)
}

// DELETE /api/v1/users/:id
func (h *UserHandler) Delete(c *gin.Context) {
    id, _ := strconv.ParseInt(c.Param("id"), 10, 64)

    if err := h.svc.DeleteUser(c.Request.Context(), id); err != nil {
        if errors.Is(err, service.ErrNotFound) {
            c.JSON(404, gin.H{"error": "用户不存在"})
            return
        }
        response.InternalError(c)
        return
    }
    c.Status(204)
}

5. API 文档:Swagger/OpenAPI

# 安装 swag 工具
go install github.com/swaggo/swag/cmd/swag@latest
go get github.com/swaggo/gin-swagger
go get github.com/swaggo/files
// @title           用户管理 API
// @version         1.0
// @description     用户管理服务 API 文档
// @host            localhost:8080
// @BasePath        /api/v1
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization

package main

// CreateUser godoc
// @Summary      创建用户
// @Description  创建一个新用户账户
// @Tags         users
// @Accept       json
// @Produce      json
// @Param        request  body      CreateUserRequest  true  "用户信息"
// @Success      201      {object}  Response{data=User}
// @Failure      400      {object}  Response
// @Failure      500      {object}  Response
// @Security     BearerAuth
// @Router       /users [post]
func createUser(c *gin.Context) { /* ... */ }

// 在路由中注册文档
import (
    ginSwagger "github.com/swaggo/gin-swagger"
    swaggerFiles "github.com/swaggo/files"
    _ "myapp/docs"  // swagger 生成的文档
)

func setupRouter() *gin.Engine {
    r := gin.Default()
    // 访问 http://localhost:8080/swagger/index.html
    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
    return r
}

// 生成文档
// $ swag init -g cmd/server/main.go
🎯

REST API 设计要点

① 使用名词复数表示资源,不用动词(/users 不用 /getUsers)  ② 版本化 API(/api/v1)  ③ 统一错误响应格式  ④ 分页用 cursor 或 page-size,不用 offset-limit(大数据集)  ⑤ 始终返回 Content-Type: application/json