分页与过滤 #

概述 #

当 API 返回大量数据时,分页和过滤是必不可少的。它们可以减少网络传输、提高响应速度、改善用户体验。

text
┌─────────────────────────────────────────────────────────────┐
│                    分页与过滤的重要性                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  不分页的问题:                                              │
│  ❌ 返回数据量过大                                          │
│  ❌ 响应时间长                                              │
│  ❌ 占用大量带宽                                            │
│  ❌ 客户端处理困难                                          │
│  ❌ 数据库压力大                                            │
│                                                             │
│  分页与过滤的好处:                                          │
│  ✅ 减少数据传输量                                          │
│  ✅ 提高响应速度                                            │
│  ✅ 降低服务器负载                                          │
│  ✅ 改善用户体验                                            │
│  ✅ 支持大数据集                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

分页方式 #

页码分页(Page-based Pagination) #

text
┌─────────────────────────────────────────────────────────────┐
│                    页码分页                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  参数:                                                      │
│  - page:页码(从 1 开始)                                  │
│  - limit/perPage:每页数量                                  │
│                                                             │
│  请求示例:                                                  │
│  GET /users?page=1&limit=20                                 │
│  GET /users?page=2&limit=20                                 │
│                                                             │
│  响应示例:                                                  │
│  {                                                          │
│    "data": [...],                                           │
│    "pagination": {                                          │
│      "page": 1,                                             │
│      "limit": 20,                                           │
│      "total": 100,                                          │
│      "totalPages": 5                                        │
│    }                                                        │
│  }                                                          │
│                                                             │
│  优点:                                                      │
│  ✅ 简单直观                                                │
│  ✅ 支持跳页                                                │
│  ✅ 可以显示总页数                                          │
│                                                             │
│  缺点:                                                      │
│  ❌ 数据变更时可能重复或遗漏                                │
│  ❌ 大偏移量性能差                                          │
│  ❌ 需要统计总数                                            │
│                                                             │
│  SQL 实现:                                                  │
│  SELECT * FROM users                                        │
│  LIMIT 20 OFFSET 0;  -- 第 1 页                             │
│  LIMIT 20 OFFSET 20; -- 第 2 页                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

偏移分页(Offset-based Pagination) #

text
┌─────────────────────────────────────────────────────────────┐
│                    偏移分页                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  参数:                                                      │
│  - offset:偏移量(从 0 开始)                              │
│  - limit:返回数量                                          │
│                                                             │
│  请求示例:                                                  │
│  GET /users?offset=0&limit=20                               │
│  GET /users?offset=20&limit=20                              │
│  GET /users?offset=40&limit=20                              │
│                                                             │
│  响应示例:                                                  │
│  {                                                          │
│    "data": [...],                                           │
│    "pagination": {                                          │
│      "offset": 0,                                           │
│      "limit": 20,                                           │
│      "total": 100                                           │
│    }                                                        │
│  }                                                          │
│                                                             │
│  优点:                                                      │
│  ✅ 灵活性高                                                │
│  ✅ 适合无限滚动                                            │
│                                                             │
│  缺点:                                                      │
│  ❌ 大偏移量性能差                                          │
│  ❌ 数据变更时可能重复或遗漏                                │
│                                                             │
│  SQL 实现:                                                  │
│  SELECT * FROM users                                        │
│  LIMIT 20 OFFSET 0;                                         │
│  LIMIT 20 OFFSET 20;                                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

游标分页(Cursor-based Pagination) #

text
┌─────────────────────────────────────────────────────────────┐
│                    游标分页                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  参数:                                                      │
│  - cursor:游标(上一页最后一条记录的标识)                  │
│  - limit:返回数量                                          │
│                                                             │
│  请求示例:                                                  │
│  GET /users?limit=20                    首页                │
│  GET /users?cursor=eyJpZCI6MjB9&limit=20 下一页             │
│                                                             │
│  响应示例:                                                  │
│  {                                                          │
│    "data": [...],                                           │
│    "pagination": {                                          │
│      "nextCursor": "eyJpZCI6NDB9",                          │
│      "previousCursor": "eyJpZCI6MH0=",                      │
│      "hasMore": true                                        │
│    }                                                        │
│  }                                                          │
│                                                             │
│  优点:                                                      │
│  ✅ 性能稳定,不受数据量影响                                │
│  ✅ 数据一致性,不会重复或遗漏                              │
│  ✅ 适合实时数据                                            │
│                                                             │
│  缺点:                                                      │
│  ❌ 不支持跳页                                              │
│  ❌ 实现复杂                                                │
│  ❌ 不能显示总页数                                          │
│                                                             │
│  SQL 实现:                                                  │
│  SELECT * FROM users                                        │
│  WHERE id > 20  -- cursor 解析出的值                        │
│  ORDER BY id ASC                                            │
│  LIMIT 20;                                                  │
│                                                             │
│  游标编码:                                                  │
│  // Base64 编码                                             │
│  const cursor = Buffer.from(JSON.stringify({ id: 20 }))    │
│    .toString('base64');                                     │
│                                                             │
│  // 解码                                                    │
│  const decoded = JSON.parse(                                │
│    Buffer.from(cursor, 'base64').toString()                 │
│  );                                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

分页方式对比 #

text
┌─────────────────────────────────────────────────────────────┐
│                    分页方式对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  特性          页码分页    偏移分页    游标分页              │
│  ─────────────────────────────────────────────────────────  │
│  简单性        ⭐⭐⭐       ⭐⭐⭐       ⭐                   │
│  性能          ⭐          ⭐          ⭐⭐⭐                 │
│  一致性        ⭐          ⭐          ⭐⭐⭐                 │
│  跳页支持      ⭐⭐⭐       ⭐⭐         ❌                   │
│  总页数        ⭐⭐⭐       ⭐⭐         ❌                   │
│  无限滚动      ⭐⭐         ⭐⭐⭐       ⭐⭐⭐                 │
│                                                             │
│  选择建议:                                                  │
│  - 管理后台、需要跳页:页码分页                             │
│  - 移动端、无限滚动:游标分页                               │
│  - 简单场景:偏移分页                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

过滤(Filtering) #

过滤参数设计 #

text
┌─────────────────────────────────────────────────────────────┐
│                    过滤参数设计                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  精确匹配:                                                  │
│  GET /users?status=active                                   │
│  GET /products?category=electronics                         │
│                                                             │
│  多值匹配:                                                  │
│  GET /users?status=active,pending                           │
│  GET /products?id=1,2,3                                     │
│                                                             │
│  范围过滤:                                                  │
│  GET /products?priceMin=100&priceMax=1000                   │
│  GET /products?price_gte=100&price_lte=1000                 │
│  GET /orders?createdAfter=2025-01-01                        │
│  GET /orders?createdBefore=2025-12-31                       │
│                                                             │
│  模糊匹配:                                                  │
│  GET /users?name_like=zhang                                 │
│  GET /products?description_like=iPhone                      │
│                                                             │
│  存在性检查:                                                │
│  GET /users?email_exists=true                               │
│  GET /products?discount_exists=false                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

过滤操作符 #

text
┌─────────────────────────────────────────────────────────────┐
│                    过滤操作符                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  操作符      说明              示例                          │
│  ─────────────────────────────────────────────────────────  │
│  _eq        等于              ?status_eq=active             │
│  _ne        不等于            ?status_ne=deleted            │
│  _gt        大于              ?price_gt=100                 │
│  _gte       大于等于          ?price_gte=100                │
│  _lt        小于              ?price_lt=1000                │
│  _lte       小于等于          ?price_lte=1000               │
│  _in        包含于            ?id_in=1,2,3                  │
│  _nin       不包含于          ?status_nin=deleted,banned    │
│  _like      模糊匹配          ?name_like=zhang              │
│  _null      为空              ?deletedAt_null=true          │
│  _nnull     不为空            ?email_nnull=true             │
│                                                             │
│  示例:                                                      │
│  GET /products?price_gte=100&price_lte=1000                 │
│  GET /users?status_in=active,pending                        │
│  GET /orders?createdAt_gte=2025-01-01                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

排序(Sorting) #

排序参数设计 #

text
┌─────────────────────────────────────────────────────────────┐
│                    排序参数设计                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  单字段排序:                                                │
│  GET /users?sort=createdAt           升序                   │
│  GET /users?sort=-createdAt          降序(- 前缀)         │
│  GET /users?sort=name&order=asc      升序                   │
│  GET /users?sort=name&order=desc     降序                   │
│                                                             │
│  多字段排序:                                                │
│  GET /users?sort=status,-createdAt                          │
│  先按状态升序,再按创建时间降序                              │
│                                                             │
│  默认排序:                                                  │
│  GET /users                    默认按 createdAt 降序        │
│                                                             │
│  响应示例:                                                  │
│  {                                                          │
│    "data": [...],                                           │
│    "sort": {                                                │
│      "field": "createdAt",                                  │
│      "order": "desc"                                        │
│    }                                                        │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

排序实现 #

text
┌─────────────────────────────────────────────────────────────┐
│                    排序实现                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  // 解析排序参数                                            │
│  function parseSort(sortParam) {                            │
│    if (!sortParam) return [['createdAt', 'DESC']];          │
│                                                             │
│    return sortParam.split(',').map(field => {               │
│      if (field.startsWith('-')) {                           │
│        return [field.slice(1), 'DESC'];                     │
│      }                                                      │
│      return [field, 'ASC'];                                 │
│    });                                                      │
│  }                                                          │
│                                                             │
│  // 示例                                                    │
│  parseSort('name,-createdAt');                              │
│  // [['name', 'ASC'], ['createdAt', 'DESC']]                │
│                                                             │
│  // SQL 构建                                                │
│  function buildOrderBy(sortFields) {                        │
│    return sortFields                                        │
│      .map(([field, order]) => `${field} ${order}`)          │
│      .join(', ');                                           │
│  }                                                          │
│                                                             │
│  // 安全验证:只允许特定字段排序                             │
│  const allowedSortFields = ['id', 'name', 'createdAt'];     │
│                                                             │
│  function validateSort(sortParam) {                         │
│    const fields = parseSort(sortParam);                     │
│    return fields.filter(([field]) =>                        │
│      allowedSortFields.includes(field)                      │
│    );                                                       │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

字段选择(Field Selection) #

字段选择设计 #

text
┌─────────────────────────────────────────────────────────────┐
│                    字段选择设计                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  指定返回字段:                                              │
│  GET /users?fields=id,name,email                            │
│  GET /products?fields=id,name,price                         │
│                                                             │
│  排除字段:                                                  │
│  GET /users?exclude=password,createdAt                      │
│                                                             │
│  嵌套字段:                                                  │
│  GET /orders?fields=id,total,user.id,user.name              │
│                                                             │
│  响应示例:                                                  │
│  GET /users?fields=id,name                                  │
│  {                                                          │
│    "data": [                                                │
│      { "id": 1, "name": "张三" },                           │
│      { "id": 2, "name": "李四" }                            │
│    ]                                                        │
│  }                                                          │
│                                                             │
│  优点:                                                      │
│  ✅ 减少数据传输量                                          │
│  ✅ 提高响应速度                                            │
│  ✅ 保护敏感字段                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

搜索(Search) #

搜索设计 #

text
┌─────────────────────────────────────────────────────────────┐
│                    搜索设计                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  简单搜索:                                                  │
│  GET /users?q=zhang                                         │
│  GET /products?search=iPhone                                │
│                                                             │
│  指定搜索字段:                                              │
│  GET /users?q=zhang&searchFields=name,email                 │
│                                                             │
│  高级搜索:                                                  │
│  POST /search                                               │
│  {                                                          │
│    "query": "iPhone",                                       │
│    "filters": {                                             │
│      "category": "electronics",                             │
│      "priceRange": { "min": 100, "max": 1000 }              │
│    },                                                       │
│    "sort": { "field": "price", "order": "asc" },            │
│    "fields": ["id", "name", "price"]                        │
│  }                                                          │
│                                                             │
│  响应示例:                                                  │
│  {                                                          │
│    "data": [...],                                           │
│    "query": "iPhone",                                       │
│    "total": 50,                                             │
│    "took": 15                                               │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

完整查询示例 #

text
┌─────────────────────────────────────────────────────────────┐
│                    完整查询示例                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  请求:                                                      │
│  GET /products?                                             │
│      page=1&                                                │
│      limit=20&                                              │
│      category=electronics&                                  │
│      price_gte=100&                                         │
│      price_lte=1000&                                        │
│      status=active&                                         │
│      sort=-price&                                           │
│      fields=id,name,price,category&                         │
│      q=iPhone                                               │
│                                                             │
│  响应:                                                      │
│  {                                                          │
│    "data": [                                                │
│      {                                                      │
│        "id": 123,                                           │
│        "name": "iPhone 15 Pro",                             │
│        "price": 999,                                        │
│        "category": "electronics"                            │
│      }                                                      │
│    ],                                                       │
│    "pagination": {                                          │
│      "page": 1,                                             │
│      "limit": 20,                                           │
│      "total": 50,                                           │
│      "totalPages": 3                                        │
│    },                                                       │
│    "filters": {                                             │
│      "category": "electronics",                             │
│      "price": { "gte": 100, "lte": 1000 },                  │
│      "status": "active"                                     │
│    },                                                       │
│    "sort": {                                                │
│      "field": "price",                                      │
│      "order": "desc"                                        │
│    },                                                       │
│    "query": "iPhone"                                        │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

分页与过滤检查清单 #

text
□ 分页
  □ 选择合适的分页方式
  □ 设置默认分页参数
  □ 限制最大返回数量
  □ 返回分页元数据

□ 过滤
  □ 支持常用过滤条件
  □ 参数命名规范
  □ 验证过滤参数
  □ 优化查询性能

□ 排序
  □ 支持多字段排序
  □ 提供默认排序
  □ 验证排序字段
  □ 防止 SQL 注入

□ 字段选择
  □ 支持指定返回字段
  □ 保护敏感字段
  □ 提供默认字段

□ 搜索
  □ 支持全文搜索
  □ 支持字段搜索
  □ 返回搜索元数据

下一步 #

现在你已经了解了分页与过滤,接下来学习 错误处理,深入了解如何设计良好的错误响应!

最后更新:2026-03-29