RESTful API 设计原则
RESTful API 设计原则
无状态性 (Stateless)
无状态是指客户端和服务器之间的通信不依赖于历史消息,服务器不存储任何会话状态。每个请求都必须包含处理该请求所需的所有信息。
违反无状态原则的例子:
- 在用户认证中使用服务器端存储的 session,通过 sessionId 来验证用户信息
- 服务器存储用户的登录状态或上下文信息
正确的做法:
- 使用 JWT (JSON Web Token) 进行用户认证
- 在请求头中携带认证信息(如 Authorization: Bearer )
- 所有必要的状态信息都应由客户端提供
优势:
- 提高可扩展性:服务器可以轻松水平扩展
- 提高可靠性:单个服务器故障不会影响用户会话
- 简化负载均衡:请求可以被路由到任何服务器
可缓存性 (Cacheability)
服务器的响应必须明确地定义自身是否可缓存,以提高性能并减轻服务器负载。
实现方式:
- 使用 HTTP 缓存头来控制缓存行为
Cache-Control: 定义缓存策略(max-age, no-cache, no-store 等)ETag: 资源版本标识符,用于条件请求Last-Modified: 资源最后修改时间
缓存策略示例:
Cache-Control: max-age=3600 # 缓存1小时
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
优势:
- 减少服务器负载
- 提高响应速度
- 降低带宽消耗
统一接口 (Uniform Interface)
统一接口是 REST 架构的核心约束,确保系统组件之间的标准化通信。
资源标识 (Resource Identification)
每个资源都通过 URI 唯一标识,客户端通过 URI 访问和操作资源。
示例:
GET /api/v1/users/123
GET /api/v1/products/456/reviews
通过表述操作资源 (Manipulation of Resources Through Representations)
客户端通过发送资源的表述(如 JSON)来操作资源,而不是直接操作资源本身。
示例:
{
"name": "John Doe",
"email": "john@example.com"
}
自描述消息 (Self-descriptive Messages)
每条消息(请求或响应)都应包含足够的信息来描述如何处理它。
实现方式:
- HTTP 方法:GET、POST、PUT、PATCH、DELETE
- HTTP 头部:Content-Type、Accept、Authorization
- HTTP 状态码:200 OK、404 Not Found 等
API 设计实践
资源命名规范
使用名词而非动词
在 REST 理念下,每个 URL 路径都代表一个资源,通过 HTTP 方法来表示操作动作。
良好示例:
- https://api.example.com/v1/zoos
- https://api.example.com/v1/animals
- https://api.example.com/v1/employees
避免的示例:
- https://api.example.com/v1/getUsers
- https://api.example.com/v1/createOrder
- https://api.example.com/v1/deleteProduct
使用复数名词
这是一个常见的约定,用于表示资源集合。
示例:
GET /users # 获取所有用户
GET /users/123 # 获取特定用户
POST /users # 创建新用户
PUT /users/123 # 更新用户信息
DELETE /users/123 # 删除用户
使用层级结构
展示资源之间的关系,但避免过深的嵌套。
良好示例:
GET /users/123/orders # 获取用户 123 的所有订单
GET /users/123/orders/456 # 获取特定用户的特定订单
避免过深嵌套:
# 避免这种过深的嵌套
GET /users/123/orders/456/items/789/reviews/101
HTTP 方法语义
| 方法 | 语义 | 幂等性 | 安全性 |
|---|---|---|---|
| GET | 获取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 更新完整资源 | 是 | 否 |
| PATCH | 部分更新资源 | 否 | 否 |
| DELETE | 删除资源 | 是 | 否 |
完整示例:
GET /zoos # 列出所有动物园
POST /zoos # 新建一个动物园
GET /zoos/123 # 获取指定动物园的信息
PUT /zoos/123 # 更新指定动物园的完整信息
PATCH /zoos/123 # 更新指定动物园的部分信息
DELETE /zoos/123 # 删除指定动物园
GET /zoos/123/animals # 列出指定动物园的所有动物
DELETE /zoos/123/animals/456 # 删除指定动物园的指定动物
版本控制 (Versioning)
在 API 设计中加入版本控制机制,确保向后兼容性和系统的可扩展性。
实现方式:
- URL 路径版本控制(推荐):
/api/v1/users - 查询参数版本控制:
/api/users?version=1 - 请求头版本控制:
Accept: application/vnd.example.v1+json
推荐使用 URL 路径版本控制:
GET /api/v1/users
POST /api/v2/users
优势:
- 支持并行开发和测试
- 提供向后兼容性
- 便于 API 演进和废弃
HTTP 状态码规范
使用标准化的 HTTP 状态码来描述请求的处理结果。
常用状态码
2xx 成功
200 OK- 请求成功201 Created- 资源创建成功204 No Content- 请求成功,无返回内容
3xx 重定向
301 Moved Permanently- 资源永久移动304 Not Modified- 资源未修改(缓存相关)
4xx 客户端错误
400 Bad Request- 请求参数错误401 Unauthorized- 未认证403 Forbidden- 无权限404 Not Found- 资源不存在409 Conflict- 资源冲突422 Unprocessable Entity- 请求格式正确但语义错误
5xx 服务器错误
500 Internal Server Error- 服务器内部错误503 Service Unavailable- 服务不可用
查询参数设计
使用查询参数进行过滤、分页、排序等操作,避免创建复杂的多级 URL。
常用查询参数模式
过滤:
GET /articles?published=true&category=tech
GET /users?status=active&role=admin
分页:
GET /users?page=2&limit=20
GET /products?offset=100&limit=50
排序:
GET /articles?sort=created_at&order=desc
GET /users?sort=name&order=asc
搜索:
GET /products?q=laptop
GET /users?search=john
响应格式标准化
API 响应应该采用结构化的 JSON 格式,保持一致性。
成功响应格式
{
"status": "success",
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
},
"meta": {
"timestamp": "2025-10-10T14:30:00Z",
"version": "1.0"
}
}
总结
RESTful API 设计是一个系统性的工程,需要综合考虑架构原则、开发实践和安全性。以下是关键要点:
核心原则
- 无状态性 - 每个请求独立,不依赖会话状态
- 可缓存性 - 合理利用缓存提高性能
- 统一接口 - 标准化通信协议和资源操作
设计要点
- 使用名词表示资源,HTTP 方法表示操作
- 采用一致的命名规范和 URL 结构
- 实现适当的版本控制策略
- 使用标准的 HTTP 状态码
- 设计清晰的查询参数模式
- 保持响应格式的一致性
最佳实践
- 优先考虑 API 的可发现性和易用性
- 确保向后兼容性
- 实施适当的安全措施
- 提供详细的文档和错误信息
遵循这些原则和实践,可以设计出健壮、可扩展且易于维护的 RESTful API。