先定义分页请求与响应结构,使用PaginationRequest接收page和limit参数并校验,PaginatedResponse封装数据、总数、当前页、每页数量及总页数;在GORM中通过Offset和Limit实现数据库分页查询,计算总页数并返回标准响应;需设置limit上限防恶意请求,处理page越界情况,建议高频接口使用缓存优化,大数据量采用游标分页提升性能;最后可封装通用Paginate函数复用逻辑。

在构建Web API时,分页是处理大量数据返回的常见需求。Golang作为高效后端语言,结合标准库和简洁结构体设计,能轻松实现分页查询与响应封装。下面介绍一种实用的分页实现方式,适用于数据库查询(如MySQL、PostgreSQL)或内存数据分页。
1. 定义分页参数与响应结构
客户端通常通过page和limit参数控制分页。我们先定义接收参数的结构体和统一的返回格式:
type PaginationRequest struct {
Page int `form:"page" binding:"gte=1"`
Limit int `form:"limit" binding:"gte=1,lte=100"`
}
type PaginatedResponse struct {
Data interface{} json:"data"
Total int64 json:"total"
Page int json:"page"
Limit int json:"limit"
TotalPages int json:"total_pages"
}
PaginationRequest用于绑定HTTP请求中的分页参数,借助binding标签做基础校验。PaginatedResponse是通用分页响应结构,包含数据、总数、当前页、每页数量和总页数。
2. 实现分页查询逻辑
以GORM为例,演示如何从数据库获取分页数据:
立即学习“go语言免费学习笔记(深入)”;
func GetUsers(c *gin.Context) {
var req PaginationRequest
if err := c.ShouldBindQuery(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 设置默认值
if req.Page == 0 {
req.Page = 1
}
if req.Limit == 0 {
req.Limit = 10
}
var users []User
var total int64
db := c.MustGet("db").(*gorm.DB)
db.Model(&User{}).Count(&total)
db.Limit(req.Limit).Offset((req.Page - 1) * req.Limit).Find(&users)
totalPages := int((total + int64(req.Limit) - 1) / int64(req.Limit))
c.JSON(200, PaginatedResponse{
Data: users,
Total: total,
Page: req.Page,
Limit: req.Limit,
TotalPages: totalPages,
})}
上述代码使用Gin框架接收查询参数,通过Offset和Limit实现SQL分页。注意总页数通过向上取整计算,避免遗漏最后一页。
3. 处理边界情况与优化建议
实际应用中需考虑以下几点:
- 对
limit设置上限(如100),防止恶意请求导致性能问题 - 当
page超出范围(如page > totalPages),可返回空数据集或报错 - 高频分页接口可引入缓存(如Redis)存储总记录数或热门页数据
-
大数据量下建议使用游标分页(cursor-based pagination),避免
OFFSET性能下降
4. 封装通用分页函数
为减少重复代码,可封装一个通用分页助手:
func Paginate(db *gorm.DB, page, limit int, data interface{}) *PaginatedResponse {
if page <= 0 {
page = 1
}
if limit <= 0 {
limit = 10
}
offset := (page - 1) * limit
var total int64
db.Model(data).Count(&total)
db.Limit(limit).Offset(offset).Find(data)
totalPages := int((total + int64(limit) - 1) / int64(limit))
return &PaginatedResponse{
Data: data,
Total: total,
Page: page,
Limit: limit,
TotalPages: totalPages,
}}
调用时只需传入数据库实例、分页参数和目标数据容器,简化控制器逻辑。
基本上就这些。Golang实现API分页不复杂但容易忽略细节,合理封装能让代码更清晰稳定。










