分页与列表接口设计

列表接口是最高频的接口形态,核心诉求就两条:能分页、能拿到总数。GoFrame ORM 的分页链式方法是 Limit(offset, limit)Page(page, size);而“查列表 + 数总数”这种组合查询,官方从 v2.5 起提供了 AllAndCount / ScanAndCount 两个便捷方法,本章结合规范路由演示完整设计。

请求与响应的分页字段

规范路由下,分页参数直接放进 Req,用校验规则约束取值范围:

package api
import "github.com/gogf/gf/v2/frame/g"

// ArticleGetListReq 列表请求:page/size 带默认值
type ArticleGetListReq struct {
    g.Meta `path:"/articles" method:"get" tags:"Article" summary:"article list"`
    Page   int `d:"1"  v:"min:1"       dc:"页码"`
    Size   int `d:"10" v:"between:1,100" dc:"每页数量"`
    Status int `dc:"状态过滤,0 不过滤"`
}

响应建议固定为“列表 + 总数 + 当前分页”,方便前端计算总页数:

type ArticleGetListRes struct {
    List  []*ArticleItem `json:"list"`
    Total int            `json:"total"`
    Page  int            `json:"page"`
    Size  int            `json:"size"`
}

type ArticleItem struct {
    Id    int    `json:"id"`
    Title string `json:"title"`
}

ORM 分页查询

package logic

import (
    "context"
    "github.com/gogf/gf/v2/frame/g"
    "project/api"
    "project/internal/dao"
)

func GetArticleList(ctx context.Context, in *api.ArticleGetListReq) (out *api.ArticleGetListRes, err error) {
    m := dao.Article.Ctx(ctx)
    if in.Status > 0 {
        m = m.Where("status", in.Status)
    }
    out = &api.ArticleGetListRes{Page: in.Page, Size: in.Size}
    // ScanAndCount:一条链完成 分页查询 + COUNT(1)
    // 第 3 个参数 false 表示总数用 COUNT(1),不携带查询字段
    err = m.Page(in.Page, in.Size).OrderDesc("id").ScanAndCount(&out.List, &out.Total, false)
    if err != nil {
        return nil, err
    }
    return out, nil
}

ScanAndCount(&list, &total, useFieldForCount) 的内部逻辑相当于 Scan 查列表 + 去掉 Limit/PageCount 总数,一步到位。也可用 AllAndCount 返回 Result(不映射结构体)的场景。传统写法 Limit(offset, size) 配合单独 Count() 仍然可用,但要多写两行与“去掉分页条件”的重复代码。

控制器接线

package v1

import (
    "context"
    "project/api"
    "project/internal/logic"
)

type Controller struct{}

func (c *Controller) GetList(ctx context.Context, req *api.ArticleGetListReq) (res *api.ArticleGetListRes, err error) {
    return logic.GetArticleList(ctx, req)
}

注册 group.Bind(new(v1.Controller)) 后,GET /articles?page=2&size=20 即返回统一包装的分页结构。

注意点

  • 不要让客户端传超大 size:校验上限 + 服务端再次封顶双保险。
  • 排序字段默认用主键倒序保证稳定;自定义排序务必白名单,防止把任意列名拼进 Order
  • Count 不计入 Limit/Page,这是 ScanAndCount 的既定行为;带 GROUP BY/子查询时总数可能不等于行数,需单独设计计数。
  • 前端“总页数”由 Total/Size 现算即可,不必冗余存储。

小结

分页接口的推荐套路:Req 里用 d 标签给默认值并加 v 校验,Res 固定 List/Total/Page/Size,查询端用 Page + ScanAndCount 一条链收尾。方法语义与版本要求(v2.5+)以官方文档 goframe.org 为准。

笔记加载中…