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端点设计与反模式设计的对比:

REST 端点设计模式 vs 反模式 ✅ 正确模式(资源式) GET /users GET /users/{id} POST /users PATCH /users/{id} DELETE /users/{id} vs ❌ 反模式(动作式) POST /getAllUsers GET /getUserById POST /createUser POST /updateUser GET /deleteUser?id=

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链接的关系:

REST 请求-响应流程与资源标识 客户端 (浏览器 / 移动端) GET /users?page=1&limit=2 API 服务器 解析资源路径 + 查询参数 执行分页查询 200 OK + JSON 响应 响应体结构 { "data": [ ... ], "_links": { "self": "/users?page=1", "next": "/users?page=2" }, "meta": { "total_items": 100 } }

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。这符合“资源集合始终存在”的语义,只是当前视图为空。