GORM + Gin 实现 RESTful 分页接口的完整实践

江屿
江屿
发布时间:2026-10-16 | 4人浏览过
原创

探讨如何使用 Go 语言的 Gin 框架与 GORM ORM 库协同工作,从结构体定义到数据库查询逻辑,逐步构建一个稳健、符合 RESTful 规范的标准化分页 API。

目标与方案概述

前端开发通常偏好基于“页码(Page)”和“每页数量(Size)”的访问方式,而后端底层操作则更依赖“偏移量(Offset)”和“限制数(Limit)”。本方案旨在通过 Go 语言的结构体标签(Tags)特性,将前端的友好请求直接映射为底层的数据库查询参数,并通过 GORM 完成数据的获取与 JSON 序列化。

第1步:设计基础分页响应结构

目的:定义统一的 API 返回格式及 URL 参数接收器。

  • 入口:model/pagination.go 或项目全局模型文件。
  • 动作:form 标签用于解析 URL Query 字符串;json 标签控制序列化字段。对于业务数据列表,推荐使用 interface{} 类型以支持不同的数据模型。
  • 重要参数:PaginationQ 包含页码(Page)、单页条数(Size)、实际查得的总数(Total)及数据列表。
// PaginationQ gin handler query binding struct
type PaginationQ struct {
    Ok     bool        `json:"ok"`
    Size   uint        `form:"size" json:"size"`       // 每页显示的数量
    Page   uint        `form:"page" json:"page"`       // 当前页码
    Data   interface{} `json:"data" comment:"muster be a pointer of slice gorm.Model"` // save pagination list
    Total  uint        `json:"total"`
}

第2步:定义数据表 Model 及搜索参数

目的:建立业务实体及其对应的搜索筛选规则。

  • 入口:model/m_ssh_log.go。
  • 动作:SshLog 结构体通过 form: 标签允许用户通过 URL 指定过滤条件(如 IP、用户名等),同时配置 gorm 关联属性。
type SshLog struct {
    BaseModel
    UserId    uint      `gorm:"index" json:"user_id" form:"user_id"`
    MachineId uint      `gorm:"index" json:"machine_id" form:"machine_id"`
    SshUser   string    `json:"ssh_user" comment:"ssh账号"`
    ClientIp  string    `json:"client_ip" form:"client_ip"`
    StartedAt time.Time `json:"started_at" form:"started_at"`
    Status    uint      `json:"status" comment:"0-未标记 2-正常 4-警告 8-危险 16-致命"`
    Remark    string    `json:"remark"`
    Log       string    `gorm:"type:text" json:"log"`
    Machine   Machine   `gorm:"association_autoupdate:false;association_autocreate:false" json:"machine"`
    User      User      `gorm:"association_autoupdate:false;association_autocreate:false" json:"user"`
}

在此模型基础上,组合出专门的查询参数:

type SshLogQ struct {
    SshLog
    PaginationQ
    FromTime string `form:"from_time"` // 搜索开始时间
    ToTime   string `form:"to_time"`   // 搜索结束时间
}

第3步:HTTP Handler 中的参数绑定

目的:拦截并校验外部传入的请求参数。

  • 入口:handler/h_ssh_log.go。
  • 动作:c.ShouldBindQuery() 会自动根据 URL Query 匹配 form tag。
  • 可观察状态:绑定成功继续执行数据库查询,失败则调用错误处理函数终止。
func SshLogAll(c *gin.Context) {
    query := &model.SshLogQ{}
    err := c.ShouldBindQuery(query) 
    if handleError(c, err) {
        return
    }
    list, total, err := query.Search()
    if handleError(c, err) {
        return
    }
    // 组装最终结果
    jsonPagination(c, list, total, &query.PaginationQ)
}

第4步:构建带有分页和过滤的查询逻辑

目的:生成动态 SQL 语句并执行。

  • 入口:model/m_ssh_log.go 方法 Search()。
  • 动作:
  1. 初始化查询对象 db.Model(...)。
  2. 根据非空字段拼接 WHERE 条件。
  3. 调用通用分页工具函数 crudAll 完成计数和提取。
func (m SshLogQ) Search() (list *[]SshLog, total uint, err error) {
    list = &[]SshLog{}
    // 预加载关联数据提升性能
    tx := db.Model(m.SshLog).Preload("User").Preload("Machine")

    // 动态搜索客户端 IP
    if m.ClientIp != "" {
        tx = tx.Where("client_ip like ?", "%"+m.ClientIp+"%")
    }

    // 时间段过滤
    if m.FromTime != "" && m.ToTime != "" {
        tx = tx.Where("`created_at` BETWEEN ? AND ?", m.FromTime, m.ToTime)
    }

    // 核心分页查询
    total, err = crudAll(&m.PaginationQ, tx, list)
    return
}

第5步:封装分页工具函数

目的:统一处理分页逻辑、缺省值修正及 JSON 输出。

逻辑说明:首先对 Page 和 Size 进行边界检查,防止出现负数或极小值;随后分别执行 Count 统计总量和利用 Limit/Offset 截取数据。

func crudAll(p *PaginationQ, queryTx *gorm.DB, list interface{}) (uint, error) {
    // 1. 设置默认参数
    if p.Size < 1 { p.Size = 10 }
    if p.Page < 1 { p.Page = 1 }

    // 2. 获取全部搜索数量
    var total uint
    err := queryTx.Count(&total).Error 
    if err != nil {
        return 0, err 
    } 

    // 计算偏移量
    offset := p.Size * (p.Page - 1)

    // 3. 获取偏移量的数据
    err = queryTx.Limit(p.Size).Offset(offset).Find(list).Error 
    if err != nil { 
        return 0, err
    } 
    
    return total, err 
}

// 4. 拼装 JSON 分页数据并返回给前端
func jsonPagination(c *gin.Context, list interface{}, total uint, query *model.PaginationQ) { 
    c.JSON(200, gin.H{
        "ok": true,
        "data": list, 
        "total": total,
        "page": query.Page, 
        "size": query.Size
    })
 } 

完成判断与效果展示

当上述代码部署完成后,开发者可使用标准的 RESTful 风格发起 GET 请求。例如:http://localhost:3333/api/ssh-log?page=1&size=10&client_ip=10.18.60.16。预期返回如下格式的 JSON 数据:

{
    "data": [
        {
            "id": 28,
            "created_at": "2019-09-12T14:25:54+08:00",
            "user_id": 26,
            "client_ip": "10.18.60.16",
            ...
        }
    ],
    "ok": true,
    "page": 1,
    "size": 10,
    "total": 1
}

简要总结

本文详细介绍了利用 Gin 强大的路由与参数绑定能力配合 GORM 优秀的查询构造器,实现了一套低维护成本、高扩展性的 RESTful 分页架构。这套模式不仅适用于 SSH 日志,也可广泛复用于各类业务实体列表接口中。

以上就是 GORM + Gin 实现 RESTful 分页接口的详细内容,更多关于 Go API 开发的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表