分页参数需用strconv.Atoi安全转换并校验边界:page默认1且不低于1,size限制10–100;SQL用ORDER BY created_at DESC + LIMIT ? OFFSET ?;返回结构体含total、hasNext等元信息;避免大OFFSET,改用游标分页。

分页参数怎么从 HTTP 请求里安全取出来
用户访问 /messages?page=2&size=10 时,page 和 size 必须转成整数并做边界校验,否则直接传给数据库会出错或被攻击。
-
page默认为 1,小于 1 的值强制设为 1 -
size建议限制在 10–100 之间,超出则用上限值(比如 50),避免拖垮数据库 - 用
strconv.Atoi转换后必须检查错误,不能忽略返回的err - 不要用
url.QueryEscape处理分页参数——那是用于生成 URL 的,不是用于解析
SQL 查询怎么写才支持 OFFSET/LIMIT 分页
Go 中用 database/sql 执行分页查询,核心是构造带 OFFSET 和 LIMIT 的语句。注意:MySQL 和 PostgreSQL 语法一致,但 SQLite 的 OFFSET 必须配合 LIMIT 使用,且不能省略 LIMIT。
-
OFFSET值 =(page - 1) * size,不是page * size - 如果用 ORM(如 GORM),别直接拼字符串,要用
Limit().Offset()链式调用 - 记得按时间倒序查(比如
ORDER BY created_at DESC),否则翻页会重复或漏数据
SELECT id, content, author, created_at FROM messages ORDER BY created_at DESC LIMIT ? OFFSET ?
如何把分页结果和元信息一起返回给前端
只返回 []Message 不够,前端需要知道总条数、当前页、每页几条、有没有下一页。建议封装一个结构体,而不是用 map[string]interface{}。
- 总条数必须单独查一次
SELECT COUNT(*) FROM messages,不能靠 len(结果切片) - 计算
totalPages := (total + size - 1) / size,用整数运算避免浮点误差 - 前端分页组件依赖
hasNext和hasPrev字段,这两个比算页码更可靠
type PageResult struct {
Data []Message `json:"data"`
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
TotalPages int `json:"total_pages"`
HasNext bool `json:"has_next"`
HasPrev bool `json:"has_prev"`
}为什么 LIMIT 10000 OFFSET 10000 性能很差
当 OFFSET 很大时(比如第 1000 页,每页 10 条),MySQL 仍要扫描前 10000 行再丢弃,I/O 和 CPU 开销陡增。
立即学习“go语言免费学习笔记(深入)”;
- 线上环境应避免深度分页,改用「游标分页(cursor-based pagination)」:用上一页最后一条记录的
created_at和id作为条件,例如WHERE created_at - 如果必须用
OFFSET,确保ORDER BY字段有索引(如INDEX(created_at, id)) - PostgreSQL 可以用
cursor或物化视图缓解,但 Go 层逻辑不变
实际部署时,最容易被忽略的是游标分页的边界处理:当两条记录 created_at 完全相同时,仅靠时间无法区分顺序,必须加入主键(如 id)做第二排序和过滤条件。










