2. Gin框架入门:安装、路由与请求响应处理

Gin 是 Go 语言生态中最流行的 Web 框架之一,以高性能、轻量级和极简 API 著称。本章将带你完成 Gin 的安装、项目初始化,并掌握核心的路由定义与请求响应处理方式,为后续构建 RESTful 服务打下基础。

2.1 安装与项目初始化

Gin 要求 Go 1.13 及以上版本。确保已正确设置 GOPATH 或使用 Go Modules 管理依赖。

2.1.1 创建项目并初始化模块

mkdir gin-demo && cd gin-demo
go mod init gin-demo

2.1.2 安装 Gin 框架

go get -u github.com/gin-gonic/gin

安装完成后,go.mod 文件会自动记录依赖版本。验证安装:

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // 默认监听 0.0.0.0:8080
}

运行 go run main.go,访问 http://localhost:8080/ping 应返回 {"message":"pong"}。

提示: gin.Default() 默认包含 Logger 和 Recovery 中间件,适合开发阶段快速启动。生产环境可改用 gin.New() 自行组合中间件。

2.2 路由定义:GET / POST / PUT / DELETE

Gin 的路由基于 Radix Tree 实现,支持精确匹配、参数路由和通配符。以下表格列出最常用的 HTTP 方法及其对应路由注册方式:

HTTP 方法 Gin 路由方法 典型用途 示例路径
GET r.GET() 获取资源 /users
POST r.POST() 创建资源 /users
PUT r.PUT() 全量更新资源 /users/:id
DELETE r.DELETE() 删除资源 /users/:id

2.2.1 完整路由示例

package main

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

func main() {
    r := gin.Default()

    // GET:查询用户列表
    r.GET("/users", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"method": "GET", "path": "/users"})
    })

    // POST:创建新用户
    r.POST("/users", func(c *gin.Context) {
        c.JSON(http.StatusCreated, gin.H{"method": "POST", "path": "/users"})
    })

    // PUT:更新指定用户(:id 为路径参数)
    r.PUT("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.JSON(http.StatusOK, gin.H{"method": "PUT", "path": "/users/" + id})
    })

    // DELETE:删除指定用户
    r.DELETE("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.JSON(http.StatusOK, gin.H{"method": "DELETE", "path": "/users/" + id})
    })

    r.Run(":8080")
}

2.2.2 路径参数与查询参数

  • 路径参数:使用 :param 语法,通过 c.Param("param") 获取。
  • 查询参数:通过 c.Query("key") 或 c.DefaultQuery("key", "default") 获取。
// 示例:GET /users/123?page=2&size=20
r.GET("/users/:id", func(c *gin.Context) {
    id   := c.Param("id")
    page := c.DefaultQuery("page", "1")
    size := c.DefaultQuery("size", "10")
    c.JSON(http.StatusOK, gin.H{
        "id":   id,
        "page": page,
        "size": size,
    })
})
注意: 路径参数和查询参数名称区分大小写。建议统一使用小写字母加下划线风格(snake_case)。

2.3 处理请求与响应

Gin 的 Context 对象封装了请求的读取和响应的写入。以下是最常用的请求数据绑定与响应格式。

2.3.1 请求数据绑定

Gin 支持自动将 JSON、表单、查询参数绑定到结构体:

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

r.POST("/users", func(c *gin.Context) {
    var user User
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusCreated, gin.H{"user": user})
})

2.3.2 响应格式

响应类型 Gin 方法 说明
JSON c.JSON(code, obj) 最常用,自动设置 Content-Type: application/json
XML c.XML(code, obj) 返回 XML 格式数据
YAML c.YAML(code, obj) 返回 YAML 格式数据
String c.String(code, format, values...) 返回纯文本
HTML c.HTML(code, name, obj) 渲染模板文件
File c.File(filepath) 返回文件下载

2.3.3 统一响应结构

为便于前端解析和后端维护,建议定义统一的响应结构:

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, message string) {
    c.JSON(httpCode, Response{
        Code:    httpCode,
        Message: message,
    })
}

2.4 Gin 请求处理流程

下图展示了 Gin 从接收 HTTP 请求到返回响应的完整内部流程:

Gin 请求处理流程 HTTP 请求到达 Engine.ServeHTTP 中间件链执行 路由匹配 + Handler 响应写入 Context 路由树查找 基于 Radix Tree Logger / Recovery 自定义中间件 ShouldBind / Param 业务逻辑处理

2.5 路由分组与模块化

当路由数量增多时,使用 Group 进行模块化管理:

func main() {
    r := gin.Default()

    // 用户相关路由组
    userGroup := r.Group("/users")
    {
        userGroup.GET("", listUsers)
        userGroup.POST("", createUser)
        userGroup.GET("/:id", getUser)
        userGroup.PUT("/:id", updateUser)
        userGroup.DELETE("/:id", deleteUser)
    }

    // 订单相关路由组
    orderGroup := r.Group("/orders")
    {
        orderGroup.GET("", listOrders)
        orderGroup.POST("", createOrder)
    }

    r.Run(":8080")
}
最佳实践: 将路由分组与版本号结合,例如 /api/v1/users,便于 API 版本演进。同时将每个资源的路由定义拆分到独立文件,保持 main.go 简洁。

2.6 本章小结

通过本章学习,你已掌握:

  • Gin 框架的安装与项目初始化流程
  • GET / POST / PUT / DELETE 四种核心路由的定义方式
  • 路径参数与查询参数的获取方法
  • 请求数据绑定与多种响应格式的使用
  • 路由分组实现模块化组织