项目文件夹

文件
2026-07-13 21:36:01 +08:00

343 行
7.4 KiB
Markdown

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
name: rest-design-patterns
description: REST 设计模式参考手册
metadata:
type: reference
---
# REST 设计模式
## 面向资源的架构
REST API 围绕资源(而非动作)构建。资源是 API 中的名词。
### 资源标识
**良好的资源 URI**
```
GET /users # 集合
GET /users/{id} # 单个资源
GET /users/{id}/orders # 嵌套集合
POST /users # 创建资源
PUT /users/{id} # 替换资源
PATCH /users/{id} # 更新资源
DELETE /users/{id} # 删除资源
```
**不良的资源 URI**
```
POST /getUser # URI 中包含动词
POST /createUser # URI 中包含动词
GET /user?action=delete # 将动作作为查询参数
```
### 资源命名约定
- 集合使用复数名词:`/users``/orders``/products`
- 使用小写字母和连字符以提高可读性:`/shipping-addresses`
- 避免深层嵌套(最多 2-3 层):`/users/{id}/orders/{orderId}`
- 使用查询参数进行过滤:`/users?status=active&role=admin`
## HTTP 方法语义
### 安全方法与幂等方法
| 方法 | 安全 | 幂等 | 使用场景 |
|--------|------|------------|----------|
| GET | 是 | 是 | 获取资源 |
| POST | 否 | 否 | 创建资源、非幂等操作 |
| PUT | 否 | 是 | 替换整个资源 |
| PATCH | 否 | 否 | 部分更新 |
| DELETE | 否 | 是 | 删除资源 |
| HEAD | 是 | 是 | 仅获取元数据 |
| OPTIONS | 是 | 是 | 获取允许的方法 |
### 方法使用
**GET —— 获取资源**
```http
GET /users/123
Accept: application/json
响应:200 OK
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-01-15T10:30:00Z"
}
```
**POST —— 创建资源**
```http
POST /users
Content-Type: application/json
{
"name": "Jane Smith",
"email": "jane@example.com"
}
响应:201 Created
Location: /users/124
{
"id": 124,
"name": "Jane Smith",
"email": "jane@example.com",
"created_at": "2024-01-16T14:20:00Z"
}
```
**PUT —— 替换资源**
```http
PUT /users/123
Content-Type: application/json
{
"name": "John Doe Updated",
"email": "john.new@example.com"
}
响应:200 OK
{
"id": 123,
"name": "John Doe Updated",
"email": "john.new@example.com",
"updated_at": "2024-01-17T09:15:00Z"
}
```
**PATCH —— 部分更新**
```http
PATCH /users/123
Content-Type: application/json
{
"email": "john.updated@example.com"
}
响应:200 OK
{
"id": 123,
"name": "John Doe",
"email": "john.updated@example.com",
"updated_at": "2024-01-17T10:00:00Z"
}
```
**DELETE —— 删除资源**
```http
DELETE /users/123
响应:204 No Content
```
## HTTP 状态码
### 成功状态码(2xx
- **200 OK** —— 请求成功(GET、PUT、PATCH
- **201 Created** —— 资源已创建(POST),需包含 Location 头部
- **202 Accepted** —— 请求已接受,待异步处理
- **204 No Content** —— 成功但无响应体(DELETE)
### 重定向(3xx
- **301 Moved Permanently** —— 资源已永久迁移
- **302 Found** —— 临时重定向
- **304 Not Modified** —— 缓存版本仍然有效
### 客户端错误(4xx
- **400 Bad Request** —— 请求语法无效或校验错误
- **401 Unauthorized** —— 需要认证或认证失败
- **403 Forbidden** —— 已认证但无权限
- **404 Not Found** —— 资源不存在
- **405 Method Not Allowed** —— 该 HTTP 方法不被资源支持
- **409 Conflict** —— 请求与当前状态冲突(例如重复)
- **422 Unprocessable Entity** —— 语法正确但语义错误
- **429 Too Many Requests** —— 请求频率超限
### 服务端错误(5xx
- **500 Internal Server Error** —— 服务器意外错误
- **502 Bad Gateway** —— 上游服务器返回无效响应
- **503 Service Unavailable** —— 服务器暂时不可用
- **504 Gateway Timeout** —— 上游服务器超时
## HATEOAS(超媒体)
### 超媒体驱动的 API
包含指向相关资源和可用操作的链接:
```json
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"_links": {
"self": { "href": "/users/123" },
"orders": { "href": "/users/123/orders" },
"update": { "href": "/users/123", "method": "PATCH" },
"delete": { "href": "/users/123", "method": "DELETE" }
}
}
```
### HAL(超文本应用语言)
```json
{
"id": 123,
"name": "John Doe",
"_links": {
"self": { "href": "/users/123" }
},
"_embedded": {
"orders": [
{
"id": 456,
"total": 99.99,
"_links": {
"self": { "href": "/orders/456" }
}
}
]
}
}
```
## 内容协商
### Accept 头部
```http
GET /users/123
Accept: application/json
GET /users/123
Accept: application/xml
GET /users/123
Accept: application/hal+json
```
### 响应 Content-Type
```http
Content-Type: application/json; charset=utf-8
Content-Type: application/problem+json
Content-Type: application/hal+json
```
## 幂等性
### 幂等操作
**PUT —— 始终幂等:**
多次相同的 PUT 请求与单次请求产生相同的结果。
**DELETE —— 幂等:**
第一次 DELETE 返回 204,后续 DELETE 返回 404(最终状态相同)。
**POST —— 默认不幂等:**
使用 `Idempotency-Key` 头部实现幂等 POST
```http
POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"amount": 100.00,
"currency": "USD"
}
```
服务端存储幂等键,对重复请求返回相同响应。
## 缓存控制
### 缓存头部
```http
Cache-Control: public, max-age=3600
Cache-Control: private, no-cache
Cache-Control: no-store
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 15 Jan 2024 10:30:00 GMT
```
### 条件请求
```http
GET /users/123
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
响应:304 Not Modified
```
```http
PUT /users/123
If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Content-Type: application/json
{
"name": "Updated Name"
}
响应:412 Precondition Failed(如果 ETag 不匹配)
```
## URI 模式
### 一致的 URI 结构
```
/{version}/{resource}
/{version}/{resource}/{id}
/{version}/{resource}/{id}/{sub-resource}
/{version}/{resource}/{id}/{sub-resource}/{sub-id}
```
### 查询参数
**过滤:**
```
GET /users?status=active&role=admin
GET /products?category=electronics&price_min=100&price_max=500
```
**排序:**
```
GET /users?sort=created_at
GET /users?sort=-created_at # 降序
GET /users?sort=name,created_at # 多字段
```
**字段选择:**
```
GET /users?fields=id,name,email
GET /users?exclude=password,social_security_number
```
**搜索:**
```
GET /users?q=john
GET /products?search=laptop
```
## 最佳实践
1. **使用名词,而非动词** —— 资源是名词,方法是动词
2. **集合使用复数** —— 使用 `/users` 而非 `/user`
3. **命名保持一致** —— 选择 snake_case 或 camelCase 并坚持使用
4. **使用正确的状态码** —— 选用恰当的 HTTP 状态码
5. **包含元数据** —— 在响应中提供分页、过滤、排序信息
6. **对 API 进行版本控制** —— 从第一天起就规划演进
7. **记录所有内容** —— OpenAPI 规范、示例、错误码
8. **默认启用安全** —— HTTPS、认证、速率限制
9. **支持过滤** —— 让客户端精确获取所需数据
10. **实现 HATEOAS** —— 使 API 具备自文档和自发现能力