**分类:** Web 协议
**阅读时间:** 约 15 分钟
**最后更新:** 2026-03-27
---
REST(Representational State Transfer,表述性状态转移)是一种软件架构风格,由 Roy Fielding 在 2000 年的博士论文中首次提出。RESTful API 是基于 REST 原则设计的 Web API,它使用标准的 HTTP 协议进行通信,具有简洁、可扩展、易于理解等特点。
| 原则 | 说明 | 重要性 |
|------|------|--------|
| 客户端 - 服务器(Client-Server) | 关注点分离,客户端负责用户界面,服务器负责数据存储 | 必需 |
| 无状态(Stateless) | 每个请求包含所有必要信息,服务器不保存会话状态 | 必需 |
| 可缓存(Cacheable) | 响应必须定义自身是否可缓存,减少网络交互 | 必需 |
| 统一接口(Uniform Interface) | 资源标识、操作标准化、自描述消息、HATEOAS | 必需 |
| 分层系统(Layered System) | 客户端无法直接判断是否连接到终端服务器 | 必需 |
| 按需代码(Code on Demand) | 服务器可传输可执行代码(如 JavaScript) | 可选 |
---
RESTful URL 设计规范
flowchart LR
subgraph Good["✅ 正确示例"]
G1["GET /users
获取用户列表"]
G2["GET /users/123
获取单个用户"]
G3["POST /users
创建用户"]
G4["PUT /users/123
完整更新用户"]
G5["DELETE /users/123
删除用户"]
end
subgraph Bad["❌ 错误示例"]
B1["GET /getUsers"]
B2["GET /user?id=123"]
B3["POST /createUser"]
B4["POST /updateUser?id=123"]
B5["POST /deleteUser?id=123"]
end
**✅ 正确示例:**
GET /users # 获取用户列表
POST /users # 创建新用户
GET /users/123 # 获取 ID 为 123 的用户
PUT /users/123 # 更新 ID 为 123 的用户
DELETE /users/123 # 删除 ID 为 123 的用户
**❌ 错误示例:**
GET /getUsers # 避免动词
POST /createUser # 避免动词
DELETE /deleteUser/123 # 避免动词
**✅ 推荐:**
/users # 用户集合
/articles # 文章集合
/orders # 订单集合
**❌ 不推荐:**
/user # 单数形式不一致
/article # 单数形式不一致
**✅ 推荐:**
/user-profiles
/order-items
/api-docs
**❌ 不推荐:**
/UserProfiles # 驼峰命名
/user_profiles # 下划线命名
/USER-PROFILES # 大写命名
**✅ 正确示例:**
GET /users/123/articles # 获取用户 123 的所有文章
GET /users/123/articles/456 # 获取用户 123 的 ID 为 456 的文章
GET /articles/456/comments # 获取文章 456 的所有评论
**注意:** 嵌套层级不宜超过 3 层,过深的嵌套会使 URL 复杂且难以维护。
---
| 方法 | 用途 | 幂等性 | 安全性 |
|------|------|--------|--------|
| GET | 获取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 更新资源(全量) | 是 | 否 |
| PATCH | 更新资源(部分) | 否* | 否 |
| DELETE | 删除资源 | 是 | 否 |
| HEAD | 获取资源元数据 | 是 | 是 |
| OPTIONS | 获取支持的方法 | 是 | 是 |
*注:PATCH 理论上可以设计为幂等,但通常不是。
GET /users/123 HTTP/1.1
Host: api.example.com
Accept: application/json
**响应示例:**
{
"id": 123,
"name": "张三",
"email": "zhangsan@example.com",
"created_at": "2026-01-15T10:30:00Z"
}
**查询参数使用:**
GET /users?role=admin&status=active&sort=created_at&order=desc
GET /articles?page=2&limit=20
GET /users?fields=id,name,email
POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"name": "李四",
"email": "lisi@example.com",
"password": "secure_password_123"
}
**成功响应(201 Created):**
HTTP/1.1 201 Created
Location: /users/456
Content-Type: application/json
{
"id": 456,
"name": "李四",
"email": "lisi@example.com",
"created_at": "2026-03-27T14:30:00Z"
}
PUT /users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"id": 123,
"name": "张三更新",
"email": "zhangsan_new@example.com",
"role": "admin"
}
**注意:** PUT 需要提供资源的完整表示,缺失的字段可能会被置为 null。
PATCH /users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"name": "张三更新"
}
**注意:** PATCH 只更新提供的字段,其他字段保持不变。
DELETE /users/123 HTTP/1.1
Host: api.example.com
**成功响应(204 No Content):**
HTTP/1.1 204 No Content
---
| 状态码 | 含义 | 使用场景 |
|--------|------|----------|
| 200 OK | 成功 | GET、PUT、PATCH 成功 |
| 201 Created | 已创建 | POST 成功创建资源 |
| 204 No Content | 无内容 | DELETE 成功,或无需返回体的操作 |
| 状态码 | 含义 | 使用场景 |
|--------|------|----------|
| 301 Moved Permanently | 永久移动 | 资源 URL 永久变更 |
| 304 Not Modified | 未修改 | 缓存验证通过 |
| 状态码 | 含义 | 使用场景 |
|--------|------|----------|
| 400 Bad Request | 错误请求 | 请求语法错误、参数无效 |
| 401 Unauthorized | 未授权 | 未提供认证信息 |
| 403 Forbidden | 禁止访问 | 有认证但无权限 |
| 404 Not Found | 未找到 | 资源不存在 |
| 405 Method Not Allowed | 方法不允许 | HTTP 方法不支持 |
| 409 Conflict | 冲突 | 资源冲突(如重复创建) |
| 422 Unprocessable Entity | 无法处理 | 语义错误(如验证失败) |
| 429 Too Many Requests | 请求过多 | 触发限流 |
| 状态码 | 含义 | 使用场景 |
|--------|------|----------|
| 500 Internal Server Error | 服务器内部错误 | 未预期的服务器错误 |
| 502 Bad Gateway | 网关错误 | 上游服务异常 |
| 503 Service Unavailable | 服务不可用 | 服务过载或维护中 |
---
**请求头:**
Content-Type: application/json
Accept: application/json
**响应头:**
Content-Type: application/json
**成功响应:**
{
"success": true,
"data": {
"id": 123,
"name": "张三"
},
"message": "操作成功"
}
**列表响应:**
{
"success": true,
"data": [
{ "id": 1, "name": "用户 1" },
{ "id": 2, "name": "用户 2" }
],
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"total_pages": 5
}
}
**错误响应:**
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "请求参数验证失败",
"details": [
{
"field": "email",
"message": "邮箱格式不正确"
},
{
"field": "password",
"message": "密码长度至少 8 位"
}
]
}
}
---
GET /api/v1/users
GET /api/v2/users
**优点:** 直观、易于缓存、浏览器地址栏可见
**缺点:** URL 较长
GET /users HTTP/1.1
Accept: application/vnd.example.v1+json
**优点:** URL 简洁
**缺点:** 不够直观、调试困难
GET /users?version=1
**优点:** 简单灵活
**缺点:** 容易被忽略、缓存困难
**最佳实践:** 推荐使用 URL 路径版本化,清晰明确。
---
**登录获取 Token:**
POST /api/v1/auth/login HTTP/1.1
Content-Type: application/json
{
"username": "admin",
"password": "password123"
}
**响应:**
{
"success": true,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
}
}
**使用 Token 访问受保护资源:**
GET /api/v1/users/123 HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
GET /api/v1/users HTTP/1.1
X-API-Key: your_api_key_here
**适用场景:** 服务端对服务端调用、公开 API
适用于第三方应用授权访问用户资源的场景。
---
GET /users?page=2&limit=20
**响应:**
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"offset": 20,
"total": 100,
"total_pages": 5
}
}
**优点:** 简单直观
**缺点:** 深度分页性能差
GET /users?cursor=eyJpZCI6MjB9&limit=20
**响应:**
{
"data": [...],
"pagination": {
"limit": 20,
"next_cursor": "eyJpZCI6NDB9",
"prev_cursor": "eyJpZCI6MTB9",
"has_more": true
}
}
**优点:** 性能好、数据一致性高
**缺点:** 无法跳转到指定页
**最佳实践:** 小数据量用偏移量分页,大数据量用游标分页。
---
GET /users?sort=created_at&order=desc
GET /articles?sort=title&order=asc
GET /users?sort=-created_at # 负号表示降序
GET /users?role=admin&status=active
GET /articles?category=tech&author=123
GET /users?name_contains=张
GET /articles?created_at_gte=2026-01-01&created_at_lt=2026-12-31
GET /users?fields=id,name,email
GET /articles?fields=title,summary,created_at
---
**问题:**
GET /getUsers # 动词 + 名词
POST /user/create # 名词 + 动词
DELETE /user/123 # 单数
**解决方案:** 统一使用复数名词,避免动词
GET /users
POST /users
DELETE /users/123
---
**问题:**
POST /users/123/delete # 用 POST 执行删除
POST /users/update # 用 POST 执行更新
**解决方案:** 使用正确的 HTTP 方法
DELETE /users/123
PUT /users/123
---
**问题:**
所有错误都返回 200,在 body 中表示错误
HTTP/1.1 200 OK
{ "success": false, "error": "Not found" }
**解决方案:** 使用正确的 HTTP 状态码
HTTP/1.1 404 Not Found
{ "error": { "code": "NOT_FOUND", "message": "资源不存在" } }
---
**问题:**
GET /users # 无版本标识
**风险:** API 变更时可能破坏现有客户端
**解决方案:** 添加版本控制
GET /api/v1/users
GET /api/v2/users
---
**问题:**
有时返回数组,有时返回对象
GET /users # 返回 [{...}, {...}]
GET /users/123 # 返回 {...}
GET /users/456 # 返回 { "data": {...} }
**解决方案:** 统一响应结构
列表
{ "data": [...], "pagination": {...} }
单个资源
{ "data": {...} }
错误
{ "error": {...} }
---
API 认证方式对比
flowchart LR
subgraph Auth["认证方式"]
K1["API Key
简单密钥验证"]
K2["Basic Auth
用户名+密码 Base64"]
K3["Bearer Token
OAuth2/JWT Token"]
K4["OAuth2.0
完整授权流程"]
end
subgraph Use["推荐场景"]
U1["内部服务间调用"]
U2["简单 API 验证"]
U3["现代 Web/Mobile App"]
U4["第三方应用授权"]
end
K1 --> U1
K2 --> U2
K3 --> U3
K4 --> U4
✅ https://api.example.com/users
❌ http://api.example.com/users
// 服务端验证示例(Node.js + Express)
app.post('/api/v1/users', (req, res) => {
const { name, email, password } = req.body;
// 验证必填字段
if (!name || !email || !password) {
return res.status(400).json({
error: { code: 'MISSING_FIELDS', message: '缺少必填字段' }
});
}
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return res.status(400).json({
error: { code: 'INVALID_EMAIL', message: '邮箱格式不正确' }
});
}
// 验证密码强度
if (password.length < 8) {
return res.status(400).json({
error: { code: 'WEAK_PASSWORD', message: '密码长度至少 8 位' }
});
}
// 继续处理...
});
X-RateLimit-Limit: 1000 # 每小时请求上限
X-RateLimit-Remaining: 998 # 剩余请求数
X-RateLimit-Reset: 1679904000 # 重置时间戳
**触发限流时:**
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
**❌ 不要在响应中返回:**
{
"id": 123,
"name": "张三",
"password": "hashed_password", // 不应返回
"credit_card": "4111-1111-1111-1111" // 不应返回
}
**✅ 正确做法:**
{
"id": 123,
"name": "张三",
"email": "zhangsan@example.com"
}
Nginx CORS 配置示例
add_header Access-Control-Allow-Origin "https://trusted-domain.com";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
add_header Access-Control-Allow-Credentials "true";
---
HTTP 方法与 CRUD 对应关系
flowchart TD
C["Create"] -->|"POST /resources"| N["201 Created"]
R["Read"] -->|"GET /resources/[id]"| OK200["200 OK"]
U["Update"] -->|"PUT /resources/[id]
PATCH /resources/[id]"| OK200b["200 OK"]
D["Delete"] -->|"DELETE /resources/[id]"| N204["204 No Content"]
style C fill:#27ae60,color:#fff
style R fill:#3498db,color:#fff
style U fill:#f39c12,color:#fff
style D fill:#e74c3c,color:#fff
| 方法 | 用途 | 幂等 | 安全 |
|------|------|------|------|
| GET | 读取资源 | ✅ | ✅ |
| POST | 创建资源 | ❌ | ❌ |
| PUT | 全量更新 | ✅ | ❌ |
| PATCH | 部分更新 | ❌ | ❌ |
| DELETE | 删除资源 | ✅ | ❌ |
| 状态码 | 含义 | 使用场景 |
|--------|------|----------|
| 200 | OK | 成功 |
| 201 | Created | 创建成功 |
| 204 | No Content | 删除成功 |
| 400 | Bad Request | 请求错误 |
| 401 | Unauthorized | 未认证 |
| 403 | Forbidden | 无权限 |
| 404 | Not Found | 未找到 |
| 429 | Too Many Requests | 限流 |
| 500 | Server Error | 服务器错误 |
| 场景 | 推荐设计 |
|------|----------|
| 获取列表 | GET /resources |
| 获取单个 | GET /resources/{id} |
| 创建 | POST /resources |
| 全量更新 | PUT /resources/{id} |
| 部分更新 | PATCH /resources/{id} |
| 删除 | DELETE /resources/{id} |
| 嵌套资源 | GET /resources/{id}/sub-resources |
---
---
---
**最后更新:** 2026-03-27
**本文字数:** 约 8500 字
**阅读时间:** 约 15 分钟