Web 开发

Gin 是 Go Web 框架,提供路由、中间件、请求绑定、数据验证和 JSON 渲染等常用能力。

go get github.com/gin-gonic/gin

路由与响应

func main() {
    router := gin.New()
    router.Use(gin.Logger(), gin.Recovery())

    router.GET("/health", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"status": "ok"})
    })

    if err := router.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}

gin.Default() 等价于创建 Engine 后注册 Logger 和 Recovery 中间件;需要精确控制中间件时使用 gin.New()

请求绑定与验证

Gin 使用 binding 标签调用内置的 validator:

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

func createUser(c *gin.Context) {
    var request CreateUserRequest
    if err := c.ShouldBindJSON(&request); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{
            "error": "invalid request",
        })
        return
    }

    c.JSON(http.StatusCreated, request)
}

ShouldBindJSON 返回错误,由 Handler 决定响应;BindJSON 验证失败时会自动写入 400,不利于统一错误格式。

路径和查询参数

router.GET("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    page := c.DefaultQuery("page", "1")

    c.JSON(http.StatusOK, gin.H{
        "id":   id,
        "page": page,
    })
})

路径参数和查询参数都是字符串,需要转换为目标类型并处理转换错误。

中间件

中间件可以在请求前后执行逻辑,并通过 Context 传递请求级数据:

func RequestID() gin.HandlerFunc {
    return func(c *gin.Context) {
        requestID := c.GetHeader("X-Request-ID")
        if requestID == "" {
            requestID = uuid.NewString()
        }

        c.Set("request_id", requestID)
        c.Header("X-Request-ID", requestID)
        c.Next()
    }
}

认证中间件拒绝请求时,应调用 c.AbortWithStatusJSON 并立即返回,防止后续 Handler 继续执行。

路由分组

api := router.Group("/api/v1")
api.Use(RequestID())
{
    api.GET("/users/:id", getUser)
    api.POST("/users", createUser)
}

admin := api.Group("/admin", Authenticate(), Authorize("admin"))
admin.DELETE("/users/:id", deleteUser)

路由分组适合统一前缀和中间件,但 Handler 中的业务逻辑仍应下沉到 Service,避免把 Gin Context 传入领域层或数据访问层。

实践建议

  • 为服务设置读取、写入和空闲超时,不要在生产环境只使用默认 http.Server 配置。
  • 统一处理参数错误、业务错误和未知错误,不要把内部错误详情直接返回给客户端。
  • 请求 Context 可通过 c.Request.Context() 传给数据库和下游请求,以支持超时与取消。
  • 使用 Recovery 防止单个请求的 panic 终止进程,同时记录堆栈并修复根因。