应使用c.query获取分页参数后,用strconv.atoi转换并校验:page默认1、size默认10且上限100,拒绝≤0值,避免sql错误;推荐游标分页替代limit offset以提升大数据量性能。

分页参数怎么从 URL 提取才安全
直接用 c.Query("page") 和 c.Query("size") 拿参数最常见,但别忘了校验。Gin 不会自动转类型,c.Query 返回的是字符串,不转换就传给数据库会 panic 或查出意外结果。
- 用
strconv.Atoi转page和size,失败时返回 400 错误,比如page=abc这种非法输入 - 设默认值:
page默认为 1,size默认为 10,但必须限制上限(比如size最大 100),防恶意请求拖垮数据库 - 负数或零要拒绝:
page 或 <code>size 都应返回错误,避免 OFFSET 传负数导致 SQL 报错或越界
MySQL 分页用 LIMIT OFFSET 还是游标分页
简单列表场景下,LIMIT ? OFFSET ? 最直白,但数据量大(比如百万级)时,OFFSET 越大性能越差——MySQL 仍要扫描前面所有行。
- 如果业务允许(如按时间/ID 递增排序),优先用游标分页:
WHERE id > ? ORDER BY id ASC LIMIT ?,快且稳定 - 用
LIMIT+OFFSET时,务必在排序字段上建索引,否则ORDER BY created_at没索引,分页会变全表扫描 - Gin 层只管传参,具体 SQL 构建交给 DAO 层,别把
OFFSET计算逻辑混进 handler
怎么把分页结果包成统一响应结构
前端需要总条数、当前页、每页数量、数据列表,这些信息不能靠前端自己拼,后端必须一次性返回。
- 定义结构体,比如:
type PageResult struct { Total int `json:"total"` Page int `json:"page"` Size int `json:"size"` Data []User `json:"data"` } - 查总数用单独 SQL(
SELECT COUNT(*) FROM users WHERE ...),别用SQL_CALC_FOUND_ROWS—— MySQL 8.0 已弃用,且不可靠 - 注意并发:总数查询和分页查询条件必须完全一致(WHERE 条件、参数绑定顺序),否则前后不一致
Gin 中如何避免分页接口被刷爆
分页接口天然容易被爬虫或恶意脚本高频调用,尤其当 size=1 时,page 一路刷到几万,数据库压力陡增。
- 在中间件里加基础限流,比如用
golang.org/x/time/rate对 IP 或用户 ID 限速 - 对
page设置硬上限(如最大只允许查到第 1000 页),超出直接 400,别让它走到 DB 查询那步 - 日志里记录异常分页请求:
page=100000、size=1这类组合,方便后续分析攻击模式
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











