revel框架中手动分页需用query.offset().limit(),前端传page(≥1)和per_page,后端计算offset=(page-1)*per_page;须校验参数、防止sql误执行、合理统计总数并缓存,推荐封装分页工具函数统一处理。

Revel 框架里用 Query.Offset().Limit() 手动分页最直接
Revel 本身不提供内置分页组件,得靠 ORM(比如 gorp 或 gorm)配合手动计算偏移量。核心就是:前端传 page 和 per_page,后端算出 offset = (page - 1) * per_page,再调 Query.Offset(offset).Limit(per_page)。
常见错误是把 page 当成从 0 开始——Revel 路由和前端习惯通常按 1 起始,错位会导致第一页漏数据或重复加载。
-
page参数必须校验是否 ≥ 1,否则Offset(-10)会 panic 或返回意外结果 - 用
gorp时注意Query是链式调用,Offset()必须在Limit()前,顺序反了会被忽略 - 别在分页前用
Query.Select(),它会立即执行查询,后续Offset/Limit失效
总数怎么查?别用 COUNT(*) 全表扫
分页必须知道总条数来算页码,但 SELECT COUNT(*) FROM table WHERE ... 在大表上很慢。如果 where 条件复杂(含 join、函数、全文检索),COUNT 可能比主查询还重。
更实际的做法是加个上限:比如只允许查前 100 页,每页 20 条,那就 SELECT COUNT(*) ... HAVING COUNT(*) ,或者用 <code>EXPLAIN 估算行数(仅限 MySQL/PostgreSQL 支持的场景)。
- 用
gorp查总数要另起一个SELECT COUNT(*)查询,不能复用原Query对象 - 如果业务允许“下一页是否存在”,可改用游标分页(
WHERE id > ? ORDER BY id LIMIT ?),完全避开总数统计 - 缓存总数时注意失效时机——插入/删除后不刷新,页码就错乱
Revel 控制器里怎么组织分页逻辑?避免重复写 offset/limit
每个列表 Action 都写一遍分页参数解析和校验太冗余。建议抽成工具函数,比如 ParsePagination(c revel.Controller) (offset, limit int, err error),统一处理默认值、范围限制和错误响应。
典型坑是没做参数类型转换:URL 里的 page=abc 会转成 0,导致 Offset(0) 返回第一页,但用户其实输错了——应该返回 400 错误而不是静默降级。
- 默认
per_page建议设为 20,上限硬编码为 100,防恶意per_page=10000拖垮 DB - 把
offset和limit作为参数传给 DAO 层,控制器不碰 SQL 组装 - 返回 JSON 时结构固定:外层包
data、total、page、per_page字段,前端不用猜字段名
前端传参和 URL 设计影响 SEO 和用户体验
用 /users?page=2&per_page=20 最通用,但 Revel 的路由匹配对 query string 不敏感,所以没问题;如果想支持 /users/2/20 这种路径式分页,就得在 routes 文件里显式定义:GET /users/{page:int}/{per_page:int} Users.Index。
注意:Revel 的 {page:int} 类型约束只校验数字格式,不检查是否 > 0,仍需在 Action 内二次判断。
- 不要把分页参数塞进 POST body——不利于分享链接、浏览器前进后退异常
- 如果列表带搜索条件,确保所有条件都拼进 URL query,否则翻页后搜索丢失
- 移动端慎用无限滚动:Revel 没有服务端推送,全靠 JS 拉接口,错误处理(如网络失败、空数据)容易被忽略
分页看着简单,但 offset/limit 的边界、总数统计的代价、参数校验的粒度,三处任一没控住,上线后就容易在高峰期暴露出性能抖动或数据错位。尤其当 where 条件变复杂时,先 explain 分页 SQL,再决定是加索引、换游标,还是加缓存。











