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
//PaginationQginhandlerquerybindingstructtypePaginationQstruct{Okbool`json:"ok"`Sizeuint`form:"size"json:"size"`Pageuint`form:"page"json:"page"`Datainterface{}`json:"data"comment:"musterbeapointerofslicegorm.Model"`//savepaginationlistTotaluint`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参数,作为搜索条件
typeSshLogstruct{BaseModelUserIduint`gorm:"index"json:"user_id"form:"user_id"`//formtag绑定ginurl-query参数MachineIduint`gorm:"index"json:"machine_id"form:"machine_id"`//formtag绑定ginurl-query参数SshUserstring`json:"ssh_user"comment:"ssh账号"`ClientIpstring`json:"client_ip"form:"client_ip"`//formtag绑定ginurl-query参数StartedAttime.Time`json:"started_at"form:"started_at"`Statusuint`json:"status"comment:"0-未标记2-正常4-警告8-危险16-致命"`Remarkstring`json:"remark"`Logstring`gorm:"type:text"json:"log"`MachineMachine`gorm:"association_autoupdate:false;association_autocreate:false"json:"machine"`UserUser`gorm:"association_autoupdate:false;association_autocreate:false"json:"user"`}
3. 定义分页查询搜索的结构体
ssh2ws/internal/h_ssh_log.go
typeSshLogQstruct{SshLogPaginationQFromTimestring`form:"from_time"`//搜索开始时间ToTimestring`form:"to_time"`//搜索结束时候}
这个结构体是提供给gin handler用作参数绑定的. 使用的方法如下:
funcSshLogAll(c*gin.Context){query:=&model.SshLogQ{}err:=c.ShouldBindQuery(query)//开始绑定url-query参数到结构体ifhandleError(c,err){return}list,total,err:=query.Search()//开始mysql业务搜索查询ifhandleError(c,err){return}//返回数据开始拼装分页jsonjsonPagination(c,list,total,&query.PaginationQ)}
4. 分页和搜索数据查询
1.创建 db-query
2.搜索非空业务字段
3.使用crudAll 方法获取数据
model/m_ssh_log.go
typeSshLogQstruct{SshLogPaginationQFromTimestring`form:"from_time"`ToTimestring`form:"to_time"`}func(mSshLogQ)Search()(list*[]SshLog,totaluint,errerror){list=&[]SshLog{}//创建db-querytx:=db.Model(m.SshLog).Preload("User").Preload("Machine")//搜索非空业务字段ifm.ClientIp!=""{tx=tx.Where("client_iplike?","%"+m.ClientIp+"%")}//搜索时间段ifm.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
funccrudAll(p*PaginationQ,queryTx*gorm.DB,listinterface{})(uint,error){//1.默认参数ifp.Size<1{p.Size=10}ifp.Page<1{p.Page=1}//2.部搜索数量vartotaluinterr:=queryTx.Count(&total).Erroriferr!=nil{return0,err}offset:=p.Size*(p.Page-1)//3.偏移量的数据err=queryTx.Limit(p.Size).Offset(offset).Find(list).Erroriferr!=nil{return0,err}returntotal,err}//4.json分页数据funcjsonPagination(c*gin.Context,listinterface{},totaluint,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.例子代码
完整项目代码地址