2. REST API 基础:RESTful 架构风格定义、资源导向设计、HTTP 方法与状态码
2.1 RESTful 架构风格定义
REST(Representational State Transfer,表述性状态转移)是由 Roy Fielding 在 2000 年博士论文中提出的一种架构风格,而非具体协议或标准。其核心思想是将网络中的一切视为资源,并通过统一的接口对资源进行无状态操作。
关键理解:REST 不是一种技术,而是一组设计约束。符合这些约束的接口被称为 RESTful API。
RESTful 架构的六大约束条件:
- 客户端-服务器(Client-Server):分离关注点,客户端不关心数据存储,服务器不关心用户界面。
- 无状态(Stateless):每个请求包含所有必要信息,服务器不保存客户端上下文。
- 可缓存(Cacheable):响应隐式或显式标记为可缓存或不可缓存,提升网络效率。
- 统一接口(Uniform Interface):通过标准方法操作资源,简化架构。
- 分层系统(Layered System):客户端无法直接感知是否与端服务器或中间层通信。
- 按需代码(Code on Demand,可选):服务器可向客户端传输可执行代码(如 JavaScript)。
2.2 资源导向设计(Resource-Oriented Design)
资源导向设计是 RESTful API 的核心设计范式。每个资源通过 URI(统一资源标识符) 唯一标识,资源的表现形式(JSON/XML/HTML)通过 Content-Type 协商确定。
资源设计原则:
- 使用名词而非动词命名资源:
/users而非/getUsers - 使用复数形式表示集合:
/orders、/products - 使用层级结构表达资源关系:
/users/{userId}/orders/{orderId} - 使用查询参数进行过滤、排序、分页:
/users?role=admin&page=1&size=20
典型资源 URI 设计示例:
| 资源类型 | URI 模式 | 说明 |
|---|---|---|
| 集合资源 | /users |
用户列表 |
| 单例资源 | /users/{id} |
单个用户详情 |
| 子集合资源 | /users/{id}/orders |
用户的订单列表 |
| 子资源 | /users/{id}/orders/{orderId} |
用户的某个订单 |
| 操作资源 | /users/{id}/activate |
用户激活(特殊情况使用动词) |
常见反模式:避免在 URI 中使用动词(
/createUser)、避免在 URI 中表达版本号(/v1/users 应通过请求头或 URL 前缀处理)、避免过深的嵌套(超过 3 层建议扁平化)。
2.3 HTTP 方法(GET/POST/PUT/DELETE)与语义
RESTful API 通过标准 HTTP 方法对资源执行 CRUD(创建、读取、更新、删除)操作。每个方法具有明确的幂等性和安全性语义。
| HTTP 方法 | CRUD 语义 | 幂等性 | 安全性 | 请求体 | 响应体 |
|---|---|---|---|---|---|
| GET | 读取资源 | ✅ 是 | ✅ 是 | 无 | 资源表示 |
| POST | 创建资源 | ❌ 否 | ❌ 否 | 资源数据 | 新资源 / 操作结果 |
| PUT | 全量更新 / 替换 | ✅ 是 | ❌ 否 | 完整资源数据 | 更新后的资源 |
| DELETE | 删除资源 | ✅ 是 | ❌ 否 | 无 | 删除状态 / 空 |
| PATCH | 部分更新 | ❌ 否 | ❌ 否 | 部分字段数据 | 更新后的资源 |
幂等性解释:多次执行同一操作,结果与执行一次相同。例如,
DELETE /users/123 无论执行 1 次还是 10 次,最终资源状态都是“已删除”。POST 每次执行都会创建新资源,因此不幂等。
实际 API 调用示例:
// 获取用户列表
GET /users
Response 200:
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob", "email": "bob@example.com" }
]
// 创建新用户
POST /users
Request Body: { "name": "Charlie", "email": "charlie@example.com" }
Response 201:
{ "id": 3, "name": "Charlie", "email": "charlie@example.com" }
// 全量更新用户
PUT /users/3
Request Body: { "name": "Charlie Updated", "email": "charlie.new@example.com" }
Response 200:
{ "id": 3, "name": "Charlie Updated", "email": "charlie.new@example.com" }
// 删除用户
DELETE /users/3
Response 204: (No Content)
2.4 HTTP 状态码(Status Codes)
HTTP 状态码是服务器对请求处理结果的标准化响应。RESTful API 应使用精确的状态码表达操作结果,而非统一返回 200。
2.5 完整请求-响应流程示例
以下展示一个完整的 RESTful API 交互流程,涵盖资源创建、查询、更新和删除:
// 1. 创建订单(POST)
POST /orders
Content-Type: application/json
{
"userId": 1,
"items": [
{ "productId": 101, "quantity": 2 },
{ "productId": 202, "quantity": 1 }
]
}
Response: 201 Created
Location: /orders/1001
{
"id": 1001,
"userId": 1,
"status": "pending",
"total": 299.99,
"createdAt": "2025-01-15T10:30:00Z"
}
// 2. 查询订单(GET)
GET /orders/1001
Response: 200 OK
{
"id": 1001,
"userId": 1,
"status": "pending",
"items": [
{ "productId": 101, "name": "Widget A", "quantity": 2, "price": 99.99 },
{ "productId": 202, "name": "Gadget B", "quantity": 1, "price": 100.01 }
],
"total": 299.99
}
// 3. 更新订单状态(PATCH)
PATCH /orders/1001
Content-Type: application/json
{ "status": "shipped" }
Response: 200 OK
{
"id": 1001,
"status": "shipped",
"updatedAt": "2025-01-15T11:00:00Z"
}
// 4. 删除订单(DELETE)
DELETE /orders/1001
Response: 204 No Content
最佳实践总结:
- 始终使用名词复数作为集合资源 URI
- 严格遵循 HTTP 方法的语义,不要用 GET 修改资源状态
- 返回精确的状态码,避免统一返回 200 然后在 body 中包装错误
- 使用 Location 头返回新创建资源的 URI(201 响应)
- 对集合资源支持分页、过滤、排序(通过查询参数)
- 使用 HATEOAS(超媒体作为应用状态引擎)提供资源导航链接(可选但推荐)