分页参数须手动从 query(用c.URLParam或c.FormValue)或JSON body(先ReadJSON再取字段)安全提取,禁用c.Params().Get;需用strconv.Atoi兜底并校验范围(如limit≤100),避免DB超时。

分页参数怎么从 HTTP 请求里安全提取
Iris 默认不自动解析 page、limit 这类分页参数,得手动从 query 或 JSON body 里取。别直接用 c.URLParam("page") 然后强转 int——URL 参数是字符串,空值或非数字会 panic。
推荐用 c.FormValue("page")(兼容 query 和 form)或 c.URLParam("page")(仅 query),再配合 strconv.Atoi + 默认值兜底:
page, err := strconv.Atoi(c.FormValue("page"))
if err != nil || page < 1 {
page = 1
}
limit, err := strconv.Atoi(c.FormValue("limit"))
if err != nil || limit < 1 || limit > 100 {
limit = 20
}- 必须校验范围:避免传
limit=999999导致 DB 查询超时 - 不要依赖
c.Params().Get("page")——那是路由参数,不是查询参数 - 如果用 JSON body 提交分页(比如 POST /search),得先调
c.ReadJSON(&req),再从结构体字段读
数据库查询怎么拼 offset/limit 才不翻车
Iris 本身不碰数据库,分页逻辑全在你选的驱动里。用 database/sql 原生写法最稳,别图省事用 ORM 的“自动分页”——很多 ORM 的 Offset().Limit() 在 MySQL 上会生成 LIMIT 10000,20,数据量大时性能断崖式下跌。
更推荐用主键游标(cursor-based)分页,尤其列表有排序时:
// 假设按 created_at DESC 排序,上一页最后一条记录的 created_at 是 "2024-05-01T10:00:00Z"
lastCreatedAt := c.FormValue("cursor")
if lastCreatedAt != "" {
// WHERE created_at < ? ORDER BY created_at DESC LIMIT ?
rows, _ := db.Query("SELECT * FROM posts WHERE created_at < ? ORDER BY created_at DESC LIMIT ?", lastCreatedAt, limit)
} else {
rows, _ := db.Query("SELECT * FROM posts ORDER BY created_at DESC LIMIT ?", limit)
}- MySQL 的
LIMIT offset, size在 offset > 10w 时极慢,游标能彻底避开 - PostgreSQL 可用
OFFSET/LIMIT,但依然建议游标;SQLite 不支持游标式分页,只能靠 offset - 别在 SQL 拼接里直接插
page和limit,必须用参数占位符防注入
怎么把分页元信息塞进 HTTP 响应头或响应体
前端需要知道总条数、当前页、是否还有下一页。Iris 不内置分页中间件,得自己算总数——但别每次查数据都 SELECT COUNT(*),它和主查询不共享索引,可能拖慢整体响应。
两种轻量做法:
- 用
SQL_CALC_FOUND_ROWS(MySQL 专属):主查询加该 hint,再跟一句SELECT FOUND_ROWS(),两次查询共用缓存 - 响应体里只返回
has_next布尔值:查limit + 1条,如果拿到limit + 1条,说明有下一页,否则没有;不暴露总页数,但足够前端做「加载更多」
常见响应结构示例(JSON body):
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"has_next": true,
"cursor": "2024-04-30T22:15:00Z"
}
}- 别把 total 放响应头(如
X-Total-Count)——跨域时要额外配Access-Control-Expose-Headers - 如果用游标分页,
cursor字段必须是服务端生成的不可篡改值(比如 base64 编码的时间戳+ID 组合),不能直接返原始时间字段
为什么用 Iris 写分页容易漏掉并发安全点
很多人把分页逻辑写在 handler 函数里,看着没问题;但一旦加了中间件(比如 JWT 鉴权)、用了 goroutine 异步查数据,c 对象就可能被复用或提前释放。
- 别在 goroutine 里直接用
c取参数或写响应——Iris 的Context不是线程安全的 - 所有分页参数必须在进入 goroutine 前提取并拷贝成局部变量(如
pageCopy := page) - 如果用
c.Next()调其他 handler,确保分页参数已解析完毕,别指望下游再重复解析
游标值尤其敏感:如果用户篡改 cursor 并发请求多个页面,数据库连接池可能被打满,得加简单限流(比如 per-IP 每秒最多 5 次分页请求)。


















