RESTful API 实践
分页、过滤、排序、错误码、重试、限流与安全这些上线前必须定好的约定。
Key Points上线前把分页方式、错误码表、重试与限流策略固定下来,客户端才能稳定对接。
分页:offset 还是 cursor
| Offset | Cursor | |
|---|---|---|
| Request | ?page=3&size=20 | ?cursor=eyJpZCI6OTAwMX0 |
| Jump to page | Yes | No |
| Deep page cost | O(n) — the DB scans and discards | O(1) — seek by key |
| Stable while data changes | No — rows shift between pages | Yes |
| Use for | Admin tables with page numbers | Feeds, infinite scroll, exports |
JSON
// cursor 分页的响应
{"items": [ ... ], "nextCursor": "eyJpZCI6ODgwMX0", "hasMore": true}过滤与排序
Text
?status=paid 等值过滤
?status=paid,shipped 多值(IN)
?minTotal=100&maxTotal=500 区间
?createdAtFrom=2026-01-01 时间范围
?sort=-createdAt,total 减号表示倒序,多个字段用逗号Whitelist sortable fields. Otherwise you expose column names and invite slow-query abuse.
错误码表
| HTTP | code | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | Malformed request |
| 401 | UNAUTHORIZED | Missing/invalid token |
| 403 | FORBIDDEN | No permission |
| 404 | NOT_FOUND | Resource missing |
| 409 | CONFLICT | Unique constraint hit |
| 422 | VALIDATION_FAILED | Field-level invalid |
| 429 | RATE_LIMITED | Too many requests |
重试与超时
- 只对幂等方法(GET/PUT/DELETE)自动重试,POST 靠幂等键
- 指数退避 + 抖动:1s、2s、4s… 上限 30s
- 只重试 5xx 与 429;4xx(除 429)重试没有意义
- 客户端超时要小于网关超时,否则重试会叠加
限流
Text
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1761883200安全清单
- 强制 HTTPS,禁止把 token 放查询参数
- 用 OAuth2 / JWT,短期 access token + 可撤销 refresh token
- 按资源做鉴权(越权是最常见漏洞),不要只校验「登录了」
- 限制请求体大小,校验 Content-Type
- 日志脱敏:手机号、身份证、token 一律打码
分页:offset 还是 cursor
| Offset 分页 | 游标分页 | |
|---|---|---|
| 请求 | ?page=3&size=20 | ?cursor=eyJpZCI6OTAwMX0 |
| 跳页 | 可以 | 不行 |
| 深翻页开销 | O(n) —— 数据库要扫过再丢弃 | O(1) —— 按键定位 |
| 数据变动时稳定 | 不稳 —— 翻页时会漏/重 | 稳定 |
| 适用 | 需要页码的后台表格 | 信息流、无限滚动、导出 |
JSON
// cursor 分页的响应
{"items": [ ... ], "nextCursor": "eyJpZCI6ODgwMX0", "hasMore": true}过滤与排序
Text
?status=paid 等值过滤
?status=paid,shipped 多值(IN)
?minTotal=100&maxTotal=500 区间
?createdAtFrom=2026-01-01 时间范围
?sort=-createdAt,total 减号表示倒序,多个字段用逗号排序字段要白名单校验,否则等于把数据库列名暴露给外部,还能被用来做慢查询。
错误码表
| HTTP | code | 含义 |
|---|---|---|
| 400 | BAD_REQUEST | 请求格式错误 |
| 401 | UNAUTHORIZED | 缺少或无效凭证 |
| 403 | FORBIDDEN | 无权限 |
| 404 | NOT_FOUND | 资源不存在 |
| 409 | CONFLICT | 唯一约束冲突 |
| 422 | VALIDATION_FAILED | 字段级校验失败 |
| 429 | RATE_LIMITED | 请求过于频繁 |
重试与超时
- 只对幂等方法(GET/PUT/DELETE)自动重试,POST 靠幂等键
- 指数退避 + 抖动:1s、2s、4s… 上限 30s
- 只重试 5xx 与 429;4xx(除 429)重试没有意义
- 客户端超时要小于网关超时,否则重试会叠加
限流
Text
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1761883200安全清单
- 强制 HTTPS,禁止把 token 放查询参数
- 用 OAuth2 / JWT,短期 access token + 可撤销 refresh token
- 按资源做鉴权(越权是最常见漏洞),不要只校验「登录了」
- 限制请求体大小,校验 Content-Type
- 日志脱敏:手机号、身份证、token 一律打码