2. REST API 基础回顾:RESTful设计原则、HTTP方法语义、资源与端点设计
在深入分页与过滤边界设计之前,必须重新校准对REST API基础的理解。许多边界问题(如过滤参数命名混乱、分页状态管理不当)的根源,恰恰是对RESTful原则的偏离。本节将系统回顾REST架构的核心约束、HTTP方法的正确语义,以及资源与端点的设计范式。
2.1 RESTful 六大架构约束
REST(Representational State Transfer)并非协议,而是一组架构约束。满足这些约束的接口才被称为RESTful。以下是六大约束及其在API设计中的体现:
| 约束 | 核心要求 | 常见违反场景 |
|---|---|---|
| 客户端-服务器 | 分离关注点,客户端不关心数据存储 | 在API响应中嵌入业务逻辑状态码 |
| 无状态 | 每个请求包含所有必要信息,服务器不保存客户端上下文 | 使用Session ID维护分页游标 |
| 可缓存 | 响应隐式或显式标记为可缓存或不可缓存 | 分页响应未设置Cache-Control头 |
| 统一接口 | 资源标识、通过表示操作资源、自描述消息、HATEOAS | 使用动作式端点(/getUsers)而非资源式(/users) |
| 分层系统 | 客户端无法区分是否直接连接终端服务器 | API网关透传内部错误栈信息 |
| 按需代码(可选) | 服务器可临时扩展客户端功能 | 极少使用,通常忽略 |
边界设计启示: 分页参数(page, limit, cursor)必须包含在请求中,不能依赖服务器端的会话状态。这是“无状态”约束在分页场景下的直接体现。
2.2 HTTP 方法语义与幂等性
每个HTTP方法在REST中都有明确的语义。错误的方法选择会导致接口语义混乱,进而影响客户端对缓存、重试、错误处理的行为判断。
| 方法 | 语义 | 幂等 | 安全 | 请求体 | 响应体 |
|---|---|---|---|---|---|
| GET | 检索资源表示 | 是 | 是 | 无(或忽略) | 资源表示 |
| POST | 创建资源或触发处理 | 否 | 否 | 资源表示 | 新资源或处理结果 |
| PUT | 完整替换资源 | 是 | 否 | 完整资源表示 | 更新后的资源 |
| PATCH | 部分更新资源 | 否(视实现而定) | 否 | 变更描述 | 更新后的资源 |
| DELETE | 移除资源 | 是 | 否 | 无 | 通常为空 |
常见误区: 使用GET方法发送请求体来传递过滤条件。虽然部分HTTP客户端支持,但这不是标准行为,代理和网关可能丢弃请求体。过滤参数应通过查询字符串(Query Parameters)传递。
2.3 资源与端点设计范式
资源是REST的核心抽象。端点设计应围绕“名词(资源)”而非“动词(动作)”展开。以下是资源设计的基本原则:
- 使用名词复数形式:
/users而非/getUser或/user。 - 层级关系通过路径表达:
/users/{userId}/orders表示用户下的订单集合。 - 避免过深的嵌套: 超过三级嵌套通常表明设计需要重新审视,可考虑使用查询参数扁平化。
- 动作通过HTTP方法表达: 激活用户应使用
PATCH /users/{userId}并传递{ "status": "active" },而非POST /users/{userId}/activate。
以下SVG图展示了标准REST端点设计与反模式设计的对比:
2.4 查询参数与过滤语义
过滤、排序、分页等操作属于“检索”范畴,应通过GET方法的查询参数表达。以下是标准的查询参数设计模式:
// 标准过滤端点设计
GET /users?status=active&role=admin
GET /users?age_gte=18&age_lte=60
GET /users?search=john&fields=id,name,email
// 排序
GET /users?sort=created_at:desc,name:asc
// 分页
GET /users?page=2&limit=20
GET /users?cursor=eyJpZCI6MTAwfQ==&limit=20
关键原则: 过滤参数不应改变资源的基本标识。
/users?status=active 仍然标识“用户集合”,只是返回了该集合的一个子集。而 /active-users 则创建了一个新的、非标准的资源标识,破坏了统一接口约束。
2.5 响应结构与HATEOAS
REST响应应包含自描述信息,使客户端能够通过响应中的链接发现后续操作。对于分页响应,HATEOAS(超媒体作为应用状态引擎)尤为重要:
{
"data": [
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob", "email": "bob@example.com" }
],
"_links": {
"self": { "href": "/users?page=1&limit=2" },
"next": { "href": "/users?page=2&limit=2" },
"prev": { "href": null },
"first": { "href": "/users?page=1&limit=2" },
"last": { "href": "/users?page=5&limit=2" }
},
"meta": {
"total_items": 10,
"total_pages": 5,
"current_page": 1
}
}
以下SVG图展示了REST请求-响应流程中,资源标识、查询参数与HATEOAS链接的关系:
2.6 状态码的正确使用
HTTP状态码是REST响应语义的重要组成部分。在分页与过滤场景中,以下状态码最为常用:
| 状态码 | 含义 | 分页/过滤场景 |
|---|---|---|
| 200 OK | 请求成功,响应体包含资源表示 | 正常返回分页数据 |
| 400 Bad Request | 请求语法错误或参数无效 | page参数为负数、limit超过最大值 |
| 404 Not Found | 资源不存在 | 请求的页码超出总页数(部分API返回200+空数组) |
| 422 Unprocessable Entity | 请求语义错误 | 过滤字段名不存在、排序字段不支持 |
| 429 Too Many Requests | 速率限制 | 客户端请求过于频繁 |
设计建议: 对于超出范围的页码,建议返回 200 OK 并附带空数组和当前页码信息,而非404。这符合“资源集合始终存在”的语义,只是当前视图为空。