Building Web APIs with Gin

Set up Gin, routing groups and parameters, middleware, JSON binding and validation, connecting MySQL with GORM, and graceful deployment of a Go web service.

IntermediateUpdated 2026-10-2758K views

Routing and Parameters

package main

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

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

    // 路由分组
    api := r.Group("/api/v1")
    {
        api.GET("/users/:id", getUser)       // 路径参数
        api.POST("/users", createUser)
    }

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

func getUser(c *gin.Context) {
    id := c.Param("id")           // 路径参数
    q := c.Query("q")             // 查询参数 ?q=go
    page := c.DefaultQuery("page", "1")
    c.JSON(http.StatusOK, gin.H{"id": id, "q": q, "page": page})
}

Binding and Validation

type UserCreate struct {
    Name  string `json:"name" binding:"required,min=2"`
    Email string `json:"email" binding:"required,email"`
    Age   int    `json:"age" binding:"gte=0,lte=150"`
}

func createUser(c *gin.Context) {
    var body UserCreate
    // ShouldBind 根据 Content-Type 自动解析 JSON/Form
    if err := c.ShouldBindJSON(&body); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusCreated, body)
}

Middleware

// 自定义中间件:计时
func timing() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()                        // 执行后续 handler
        fmt.Printf("%s %s %v\n", c.Request.Method, c.Request.URL.Path, time.Since(start))
    }
}

r.Use(timing())                     // 全局
api.Use(authRequired())             // 仅 /api/v1

// JWT 鉴权示例(伪代码)
func authRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        claims, err := parseJWT(token)
        if err != nil {
            c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
            return
        }
        c.Set("uid", claims.UID)     // 在 handler 中 c.MustGet("uid")
        c.Next()
    }
}

GORM and MySQL

import "gorm.io/gorm"
import "gorm.io/driver/mysql"

type Product struct {
    gorm.Model           // ID, CreatedAt, UpdatedAt, DeletedAt
    Name  string `gorm:"size:128;not null"`
    Price float64
}

db, err := gorm.Open(mysql.Open(
    "user:pass@tcp(127.0.0.1:3306)/shop?charset=utf8mb4&parseTime=True"),
    &gorm.Config{})

db.AutoMigrate(&Product{})          // 建表/迁移
db.Create(&Product{Name: "Go Book", Price: 59})

var p Product
db.First(&p, 1)                    // 主键查询
db.Model(&p).Update("price", 49)
db.Where("price > ?", 50).Find(&list)

Graceful Shutdown

srv := &http.Server{Addr: ":8080", Handler: r}
go func() {
    if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
        log.Fatal(err)
    }
}()

quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit                       // 等待退出信号

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)           // 处理完已有连接再退出

Compile with CGO_ENABLED=0 go build -ldflags='-s -w' to get a small static binary (often 10-20MB) that runs on any Linux — no runtime installation needed.

Friends & Partners