Go语言分页需安全解析HTTP参数并校验page≥1、size在1–100间,用OFFSET/LIMIT时注意高偏移性能问题,推荐游标分页;响应应含Total、Page、TotalPages等元信息。

Go 语言本身不提供开箱即用的分页组件,但通过组合 database/sql(或 gorm)、HTTP 请求参数解析和结构化响应,能简洁、可控地实现分页——关键不是“用什么库”,而是怎么设计 offset/limit 逻辑和处理边界。
如何从 HTTP 请求中安全提取 page 和 size 参数
用户传来的 page 和 size 是字符串,必须显式校验并转为整数,否则直接用于 SQL 会导致 panic 或注入风险(即使用了预处理,非法值仍可能破坏业务逻辑)。
-
page应 ≥ 1,0或负数视为第 1 页;size建议限制范围(如 1–100),超限则截断或返回错误 - 用
strconv.ParseInt()而非strconv.Atoi(),便于区分空值和格式错误 - 推荐封装成可复用函数,例如:
func parsePageQuery(r *http.Request) (page, size int64, err error) { page, err = strconv.ParseInt(r.URL.Query().Get("page"), 10, 64) if err != nil || page < 1 { page = 1 } size, err = strconv.ParseInt(r.URL.Query().Get("size"), 10, 64) if err != nil || size < 1 { size = 10 } if size > 100 { size = 100 } return }
使用 OFFSET/LIMIT 实现数据库分页的注意事项
MySQL/PostgreSQL/SQLite 都支持 LIMIT offset, count,但高偏移量(如 OFFSET 100000)性能会急剧下降——数据库仍需扫描并跳过前面所有行。
- 避免在总记录数未知时盲目用
OFFSET:先查总数(SELECT COUNT(*))再分页,适合中小数据集;大数据量建议用游标分页(基于上一页最后 ID 或时间戳) - 计算
offset时注意:第 1 页对应offset = 0,公式是(page - 1) * size,别写成page * size - 若用
gorm,调用Limit(size).Offset(offset)即可,但需确保offset非负;Limit(0)会被忽略,Offset(0)安全
构造带分页元信息的 JSON 响应结构
前端需要知道当前页、总页数、总数、是否有下一页等,不能只返回数据列表。硬编码字段名易出错,建议定义结构体统一序列化。
立即学习“go语言免费学习笔记(深入)”;
- 典型响应结构示例:
type PageResult[T any] struct { Data []T `json:"data"` Total int64 `json:"total"` Page int64 `json:"page"` Size int64 `json:"size"` TotalPages int64 `json:"total_pages"` HasNext bool `json:"has_next"` HasPrev bool `json:"has_prev"` } -
TotalPages计算用(total + size - 1) / size(向上取整),避免浮点运算 -
HasNext=page * size ,HasPrev=page > 1
真正麻烦的不是写几行 LIMIT,而是当 page=10000 且每页 20 条时,OFFSET 199980 会让查询变慢甚至超时——这时候得切到基于主键或时间戳的游标分页,而不仅仅是改个参数。










