2. API设计原则:RESTful设计核心原则、资源命名规范、HTTP方法语义与状态码选择
RESTful API 设计并非一套僵化的协议,而是一组基于 Web 架构风格的约束与最佳实践。本章聚焦于三个核心维度:资源建模(将业务实体抽象为资源)、统一接口(通过标准 HTTP 方法操作资源)以及无状态通信(每个请求包含完整上下文)。掌握这些原则是构建可维护、可演进 API 的基础。
2.1 RESTful 设计核心原则
REST(Representational State Transfer)由 Roy Fielding 在其博士论文中提出,其核心约束包括:
- 资源标识:每个资源(如用户、订单)通过唯一的 URI 标识。
- 通过表征操作资源:客户端通过资源的表征(如 JSON、XML)与服务端交互。
- 自描述消息:每个请求和响应包含足够的信息(如媒体类型、缓存指令)以理解如何处理。
- 超媒体作为应用状态引擎(HATEOAS):响应中包含链接,引导客户端发现后续操作。
实践要点:在工程落地中,前三条原则应作为硬性约束严格执行;HATEOAS 可根据团队成熟度逐步引入,不必强求一步到位。
下图展示了 RESTful 架构中客户端、资源与表征之间的核心交互流程:
2.2 资源命名规范
资源命名是 API 设计的“第一印象”,直接影响可读性和可发现性。以下是经过大量工程验证的命名规范:
| 规范类别 | 推荐做法 | 反例 | 说明 |
|---|---|---|---|
| 名词复数 | /users, /orders |
/getUser, /userList |
资源集合使用复数名词,避免动词 |
| 小写字母 | /api/v2/products |
/API/V2/Products |
统一小写,避免大小写敏感问题 |
| 连字符分隔 | /order-items |
/order_items 或 /orderItems |
使用连字符 - 提升可读性 |
| 层级关系 | /users/{userId}/orders |
/getOrdersByUser?uid=123 |
通过路径层级表达资源从属关系 |
| 查询参数过滤 | /orders?status=active |
/orders/active |
筛选条件放在查询参数,而非路径 |
常见陷阱:避免在 URI 中使用动词(如
/createUser),操作语义应由 HTTP 方法表达。同时,避免过深的嵌套层级(超过三级),这会增加客户端耦合度。
2.3 HTTP 方法语义
HTTP 方法定义了客户端对资源期望执行的操作。RESTful API 应严格遵循每种方法的语义:
| HTTP 方法 | CRUD 映射 | 幂等性 | 安全性 | 请求体 | 响应体 |
|---|---|---|---|---|---|
| GET | 读取 (Read) | 是 | 是 | 无 | 资源表征 |
| POST | 创建 (Create) | 否 | 否 | 资源表征 | 新资源表征或操作结果 |
| PUT | 全量替换 (Update) | 是 | 否 | 完整资源表征 | 更新后的资源表征 |
| PATCH | 部分更新 (Partial Update) | 否 | 否 | 差异描述(如 JSON Patch) | 更新后的资源表征 |
| DELETE | 删除 (Delete) | 是 | 否 | 无 | 通常为空 |
关键区分:
PUT 要求客户端发送完整的资源状态,服务端用此状态完全替换现有资源;PATCH 仅发送需要变更的字段。误用 PUT 进行部分更新是常见的设计缺陷。
2.4 状态码选择
状态码是服务端向客户端传达操作结果的“第一语言”。选择正确的状态码能显著提升 API 的自描述能力。以下是 RESTful API 中最常用的状态码分类:
| 分类 | 状态码 | 含义 | 典型场景 |
|---|---|---|---|
| 2xx 成功 | 200 OK |
请求成功 | GET 获取资源、PUT 更新成功 |
201 Created |
资源创建成功 | POST 创建新资源,响应应包含 Location 头 |
|
204 No Content |
操作成功,无返回内容 | DELETE 删除成功、某些 PUT/PATCH 操作 | |
| 4xx 客户端错误 | 400 Bad Request |
请求格式错误或参数无效 | JSON 解析失败、必填字段缺失 |
401 Unauthorized |
未提供身份认证或认证失败 | 缺少 Token 或 Token 过期 | |
403 Forbidden |
已认证但无权限 | 普通用户尝试访问管理员接口 | |
404 Not Found |
资源不存在 | 请求的 URI 对应的资源未找到 | |
| 5xx 服务端错误 | 500 Internal Server Error |
服务端内部错误 | 数据库连接失败、未捕获异常 |
503 Service Unavailable |
服务暂时不可用 | 系统过载、维护中 |
状态码选择原则:优先使用标准状态码,避免自定义状态码。对于业务逻辑错误(如余额不足),使用
400 Bad Request 或 422 Unprocessable Entity,并在响应体中提供详细的错误信息,而非滥用 200 OK 包裹错误。
下图展示了 API 请求从发起到响应的完整状态码决策流程:
2.5 综合示例:用户资源 API
以下示例综合展示了资源命名、HTTP 方法和状态码的协同使用:
# 创建用户
POST /api/v2/users
Content-Type: application/json
{
"name": "张三",
"email": "zhangsan@example.com"
}
→ 201 Created
Location: /api/v2/users/12345
# 获取用户列表
GET /api/v2/users?page=1&size=20
→ 200 OK
{
"data": [...],
"total": 100,
"page": 1
}
# 获取单个用户
GET /api/v2/users/12345
→ 200 OK
{
"id": "12345",
"name": "张三",
"email": "zhangsan@example.com"
}
# 全量更新用户
PUT /api/v2/users/12345
Content-Type: application/json
{
"name": "李四",
"email": "lisi@example.com"
}
→ 200 OK
# 部分更新用户邮箱
PATCH /api/v2/users/12345
Content-Type: application/json
{
"email": "newemail@example.com"
}
→ 200 OK
# 删除用户
DELETE /api/v2/users/12345
→ 204 No Content
# 权限不足示例
GET /api/v2/admin/settings
→ 403 Forbidden
{
"error": "FORBIDDEN",
"message": "当前用户无管理员权限"
}
# 资源不存在示例
GET /api/v2/users/99999
→ 404 Not Found
{
"error": "NOT_FOUND",
"message": "用户 99999 不存在"
}
设计检查清单:每次设计新 API 端点时,对照以下问题自检:
1. URI 是否使用名词复数且全小写?
2. HTTP 方法是否准确表达了操作语义?
3. 成功时是否返回了正确的 2xx 状态码?
4. 错误时是否返回了对应的 4xx/5xx 状态码并附带了可读的错误信息?
5. 是否避免了在 URI 中使用动词或查询参数表达操作?
1. URI 是否使用名词复数且全小写?
2. HTTP 方法是否准确表达了操作语义?
3. 成功时是否返回了正确的 2xx 状态码?
4. 错误时是否返回了对应的 4xx/5xx 状态码并附带了可读的错误信息?
5. 是否避免了在 URI 中使用动词或查询参数表达操作?