Announcement

👇Official Account👇

图片

Welcome to join the group & private message

Article first/tail QR code

Skip to content

Gin 框架实战指南:从入门到生产级 API

Gin 是目前 GitHub Star 最多的 Go Web 框架(7万+),以其极高的性能和简洁的 API 著称。基准测试中,Gin 的路由性能比标准库 net/http 快约 40 倍。

一、安装与快速开始

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

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

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

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "pong",
            "code":    200,
        })
    })

    r.Run(":8080") // 启动服务
}

二、路由

2.1 HTTP 方法

go
r.GET("/users", listUsers)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.DELETE("/users/:id", deleteUser)
r.PATCH("/users/:id", patchUser)

// 任意方法
r.Any("/any", anyHandler)

2.2 路径参数与查询参数

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

// 通配符参数
r.GET("/files/*filepath", func(c *gin.Context) {
    path := c.Param("filepath") // 包含前缀 /
    c.JSON(200, gin.H{"path": path})
})

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

2.3 路由分组

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

    articles := v1.Group("/articles")
    {
        articles.GET("", listArticles)
        articles.POST("", createArticle)
    }
}

v2 := r.Group("/api/v2")
{
    // v2 接口...
}

三、请求绑定与验证

Gin 集成了 go-playground/validator,支持丰富的验证规则。

go
// 定义请求结构体
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:"gte=1,lte=150"`
    Role     string `json:"role" binding:"oneof=admin user guest"`
}

// 处理器
func createUser(c *gin.Context) {
    var req CreateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{
            "error": err.Error(),
        })
        return
    }

    // 业务逻辑...
    c.JSON(http.StatusCreated, gin.H{
        "message": "创建成功",
        "data":    req,
    })
}

// 表单绑定
type LoginForm struct {
    Username string `form:"username" binding:"required"`
    Password string `form:"password" binding:"required"`
}

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

// 查询参数绑定
type PageQuery struct {
    Page int `form:"page" binding:"gte=1"`
    Size int `form:"size" binding:"gte=1,lte=100"`
}

func listItems(c *gin.Context) {
    var q PageQuery
    q.Page = 1
    q.Size = 20
    if err := c.ShouldBindQuery(&q); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
}

自定义验证器

go
import "github.com/go-playground/validator/v10"

// 注册自定义验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    v.RegisterValidation("phone", validatePhone)
}

func validatePhone(fl validator.FieldLevel) bool {
    phone := fl.Field().String()
    matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, phone)
    return matched
}

// 使用自定义验证器
type PhoneRequest struct {
    Phone string `json:"phone" binding:"required,phone"`
}

四、中间件

4.1 内置中间件

go
r := gin.New()           // 不带任何中间件
r := gin.Default()       // 带 Logger + Recovery

// Logger:记录请求日志
r.Use(gin.Logger())

// Recovery:从 panic 恢复
r.Use(gin.Recovery())

4.2 自定义中间件

go
// 请求 ID 中间件
func RequestID() gin.HandlerFunc {
    return func(c *gin.Context) {
        requestID := c.GetHeader("X-Request-ID")
        if requestID == "" {
            requestID = uuid.New().String()
        }
        c.Set("requestID", requestID)
        c.Header("X-Request-ID", requestID)
        c.Next()
    }
}

// 耗时统计中间件
func Timing() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()
        duration := time.Since(start)
        log.Printf("[%s] %s %v", c.Request.Method, c.Request.URL.Path, duration)
    }
}

// 跨域中间件
func CORS() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Header("Access-Control-Allow-Origin", "*")
        c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH,OPTIONS")
        c.Header("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Request-ID")

        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }
        c.Next()
    }
}

// 限流中间件(令牌桶)
func RateLimit(qps float64) gin.HandlerFunc {
    limiter := rate.NewLimiter(rate.Limit(qps), int(qps))
    return func(c *gin.Context) {
        if !limiter.Allow() {
            c.AbortWithStatusJSON(429, gin.H{
                "error": "请求过于频繁,请稍后再试",
            })
            return
        }
        c.Next()
    }
}

// 注册全局中间件
r.Use(RequestID())
r.Use(CORS())
r.Use(Timing())

// 仅对某个路由组生效
api := r.Group("/api")
api.Use(RateLimit(100))

4.3 JWT 认证中间件

go
import "github.com/golang-jwt/jwt/v5"

type Claims struct {
    UserID uint   `json:"userId"`
    Role   string `json:"role"`
    jwt.RegisteredClaims
}

var jwtSecret = []byte("your-secret-key")

// 生成 Token
func GenerateToken(userID uint, role string) (string, error) {
    claims := Claims{
        UserID: userID,
        Role:   role,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
            Issuer:    "friday-go.icu",
        },
    }
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(jwtSecret)
}

// JWT 中间件
func JWTAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
            c.AbortWithStatusJSON(401, gin.H{"error": "缺少认证 Token"})
            return
        }

        tokenString := strings.TrimPrefix(authHeader, "Bearer ")
        token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {
            return jwtSecret, nil
        })
        if err != nil || !token.Valid {
            c.AbortWithStatusJSON(401, gin.H{"error": "Token 无效或已过期"})
            return
        }

        claims := token.Claims.(*Claims)
        c.Set("userID", claims.UserID)
        c.Set("role", claims.Role)
        c.Next()
    }
}

// 使用
authGroup := r.Group("/api/v1")
authGroup.Use(JWTAuth())
{
    authGroup.GET("/profile", getProfile)
    authGroup.PUT("/profile", updateProfile)
}

五、统一响应格式

go
// 定义统一响应结构
type Response struct {
    Code    int         `json:"code"`
    Message string      `json:"message"`
    Data    interface{} `json:"data,omitempty"`
}

func Success(c *gin.Context, data interface{}) {
    c.JSON(http.StatusOK, Response{
        Code:    0,
        Message: "success",
        Data:    data,
    })
}

func Error(c *gin.Context, httpCode int, code int, msg string) {
    c.JSON(httpCode, Response{
        Code:    code,
        Message: msg,
    })
}

// 使用
func getUser(c *gin.Context) {
    id := c.Param("id")
    user, err := userService.FindByID(id)
    if err != nil {
        if errors.Is(err, ErrNotFound) {
            Error(c, 404, 10004, "用户不存在")
            return
        }
        Error(c, 500, 50000, "服务器内部错误")
        return
    }
    Success(c, user)
}

六、文件上传

go
// 单文件上传
r.POST("/upload", func(c *gin.Context) {
    file, err := c.FormFile("file")
    if err != nil {
        c.JSON(400, gin.H{"error": "文件获取失败"})
        return
    }

    // 校验文件大小(10MB 限制)
    if file.Size > 10*1024*1024 {
        c.JSON(400, gin.H{"error": "文件不能超过 10MB"})
        return
    }

    // 校验文件类型
    ext := filepath.Ext(file.Filename)
    allowed := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".gif": true}
    if !allowed[strings.ToLower(ext)] {
        c.JSON(400, gin.H{"error": "不支持的文件类型"})
        return
    }

    // 生成唯一文件名
    filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
    dst := filepath.Join("uploads", filename)

    if err := c.SaveUploadedFile(file, dst); err != nil {
        c.JSON(500, gin.H{"error": "文件保存失败"})
        return
    }

    c.JSON(200, gin.H{
        "url":      "/uploads/" + filename,
        "filename": filename,
    })
})

// 多文件上传
r.POST("/uploads", func(c *gin.Context) {
    form, _ := c.MultipartForm()
    files := form.File["files[]"]
    var urls []string
    for _, file := range files {
        filename := filepath.Base(file.Filename)
        dst := filepath.Join("uploads", filename)
        c.SaveUploadedFile(file, dst)
        urls = append(urls, "/uploads/"+filename)
    }
    c.JSON(200, gin.H{"urls": urls})
})

七、优雅关机

go
func main() {
    r := gin.Default()
    // ... 注册路由

    srv := &http.Server{
        Addr:    ":8080",
        Handler: r,
    }

    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("启动失败: %v\n", err)
        }
    }()
    log.Println("服务器启动,监听 :8080")

    // 等待中断信号
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    log.Println("正在关闭服务器...")

    // 给正在处理的请求 5 秒完成时间
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("服务器强制关闭:", err)
    }

    log.Println("服务器已关闭")
}

八、项目结构推荐

project/
├── main.go
├── config/
│   └── config.go          # 配置管理
├── router/
│   └── router.go          # 路由注册
├── middleware/
│   ├── auth.go            # JWT 中间件
│   ├── cors.go            # 跨域
│   └── ratelimit.go       # 限流
├── handler/               # HTTP 处理器(薄层)
│   ├── user.go
│   └── article.go
├── service/               # 业务逻辑
│   ├── user.go
│   └── article.go
├── repository/            # 数据库操作
│   ├── user.go
│   └── article.go
├── model/                 # 数据模型
│   ├── user.go
│   └── article.go
└── pkg/                   # 公共工具
    ├── response/
    └── logger/

总结

Gin 的核心优势:

特性说明
性能基于 httprouter,路由极快
中间件链式调用,扩展方便
参数绑定自动反序列化 + 验证
错误处理Recovery 防止 panic 崩溃
生态丰富的第三方扩展

作者:PFinal南丞 | 更新时间:2026-04-21

上次更新于: