乘风原创程序

  • gorm+gin实现restful分页接口的实践
  • 2021/12/22 11:19:26
  • api处理分页看似简单,实际上暗藏危机.最常见的分页方式,大概是下面这样的

    • 页数表示法:/user/?page=1&size=15&name=李
    • 偏移量表示法:/user/?offset=100&limit=15&name=李

    使用页码表示法对前端开发比较友好,但是本质上是和偏移量表示发相似. 在这里我们将使用?jinzhu/gorm?gin-gonic/gin?开发一个简单的分页接口

    分页查询url:?http://dev.mojotv.cn:3333/api/ssh-log?client_ip=&page=1&size=10&user_id=0&machine_id=0?返回json 结果

    {
        "data": [
            {
                "id": 28,
                "created_at": "2019-09-12t14:25:54+08:00",
                "updated_at": "2019-09-12t14:25:54+08:00",
                "user_id": 26,
                "machine_id": 1,
                "ssh_user": "mojotv.cn",
                "client_ip": "10.18.60.16",
                "started_at": "2019-09-12t14:24:05+08:00",
                "status": 0,
                "remark": ""
            }
        ],
        "ok": true,
        "page": 1,
        "size": 10,
        "total": 1
    }

    1. 定义分页struct

    //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"`
    }
    
    • ok?代表业务查询没有出错
    • size?每页显示的数量,使用?form?tag 接受gin的url-query参数
    • page?当前页码,使用?form?tag 接受gin的url-query参数
    • data?分页的数据内容
    • total?全部的页码数量

    2. 数据表model

    这里以ssh_log(ssh 命令日志为示例),使用gorm创建mysql数据表模型, 使用?form?tag 接受gin的url-query参数,作为搜索条件

    type sshlog struct {
     basemodel
     userid    uint      `gorm:"index" json:"user_id" form:"user_id"` //form tag 绑定gin url-query 参数
     machineid uint      `gorm:"index" json:"machine_id" form:"machine_id"` //form tag 绑定gin url-query 参数
     sshuser   string    `json:"ssh_user" comment:"ssh账号"`
     clientip  string    `json:"client_ip" form:"client_ip"` //form tag 绑定gin url-query 参数
     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"`
    }
    

    3. 定义分页查询搜索的结构体

    ssh2ws/internal/h_ssh_log.go
    type sshlogq struct {
     sshlog
     paginationq
     fromtime string `form:"from_time"` //搜索开始时间
     totime   string `form:"to_time"`  //搜索结束时候
    }
    

    这个结构体是提供给gin handler用作参数绑定的. 使用的方法如下:

    func sshlogall(c *gin.context) {
     query := &model.sshlogq{}
     err := c.shouldbindquery(query) //开始绑定url-query 参数到结构体
     if handleerror(c, err) {
      return
     }
     list, total, err := query.search()  //开始mysql 业务搜索查询
     if handleerror(c, err) {
      return
     }
     //返回数据开始拼装分页json
     jsonpagination(c, list, total, &query.paginationq)
    }
    

    4. 分页和搜索数据查询

    1.创建 db-query
    2.搜索非空业务字段
    3.使用crudall 方法获取数据

    model/m_ssh_log.go
    type sshlogq struct {
     sshlog
     paginationq
     fromtime string `form:"from_time"`
     totime   string `form:"to_time"`
    }
    
    func (m sshlogq) search() (list *[]sshlog, total uint, err error) {
     list = &[]sshlog{}
     //创建 db-query
     tx := db.model(m.sshlog).preload("user").preload("machine")
     //搜索非空业务字段
     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)
     }
     //使用crudall 方法获取数据
     total, err = crudall(&m.paginationq, tx, list)
     return
    }
    
    

    crudall 方法来构建sql分页数据,

    • 设置默认参数
    • 获取全部搜索数量
    • 获取偏移量的数据
    • 拼装json 分页数据?

    model/helper.go

    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.abortwithstatusjson(200, gin.h{
            “ok”: true,
            “data”: list, 
            “total”: total,
            “page”: query.page, 
            “size”: query.size
        })
     } 

    api处理分页看似简单,实际上暗藏危机.最常见的分页方式,大概是下面这样的

    • 页数表示法:/user/?page=1&size=15&name=李
    • 偏移量表示法:/user/?offset=100&limit=15&name=李

    使用页码表示法对前端开发比较友好,但是本质上是和偏移量表示发相似. 在这里我们将使用?jinzhu/gorm和?gin-gonic/gin?开发一个简单的分页接口

    分页查询url:?http://dev.mojotv.cn:3333/api/ssh-log?client_ip=&page=1&size=10&user_id=0&machine_id=0?返回json 结果

    {
        "data": [
            {
                "id": 28,
                "created_at": "2019-09-12t14:25:54+08:00",
                "updated_at": "2019-09-12t14:25:54+08:00",
                "user_id": 26,
                "machine_id": 1,
                "ssh_user": "mojotv.cn",
                "client_ip": "10.18.60.16",
                "started_at": "2019-09-12t14:24:05+08:00",
                "status": 0,
                "remark": ""
            }
        ],
        "ok": true,
        "page": 1,
        "size": 10,
        "total": 1
    }
    

    5.例子代码

    完整项目代码地址

    到此这篇关于gorm+gin实现restful分页接口的实践的文章就介绍到这了,更多相关gorm gin restful分页接口内容请搜索本教程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持本教程网!