RESTful API 设计:构建可扩展 API 的最佳实践
I’m sorry, but I don’t have the ability to retrieve the content from the linked article. If you can provide the text you’d like translated, I’ll be happy to translate it into Simplified Chinese while preserving the formatting and technical terms.
资源命名
✅ 好 – 资源使用名词
GET /users # 列出用户
POST /users # 创建用户
GET /users/123 # 获取用户
PUT /users/123 # 更新用户
DELETE /users/123 # 删除用户
❌ 差 – URL 中出现动词
GET /getUsers
POST /createUser
GET /getUserById/123
用于关系的嵌套资源
GET /users/123/orders # 用户的订单
GET /users/123/orders/456 # 指定订单
POST /users/123/orders # 为用户创建订单
替代方案:使用查询参数进行过滤
GET /orders?user_id=123 # 按用户过滤订单
经验法则: 集合使用 复数名词(
/users),具体资源使用 单数标识符(/users/123)。
命名约定
-
kebab‑case 用于多词资源
GET /user-profiles GET /order-items GET /shipping-addresses -
snake_case 用于查询参数
GET /products?category_id=5&sort_by=price&sort_order=desc
HTTP 方法
| 方法 | 目的 | 幂等? | 可缓存? |
|---|---|---|---|
GET | 检索资源 | ✅ | ✅ |
POST | 创建新资源 | ❌ | ❌ |
PUT | 完全替换资源 | ✅ | ❌ |
PATCH | 部分更新 | ❌ | ❌ |
DELETE | 删除资源 | ✅ | ❌ |
OPTIONS | 发现允许的方法 | ✅ | ✅ |
HEAD | 仅检索头部 | ✅ | ✅ |
状态码
成功
200 OK # Successful GET, PUT, PATCH
201 Created # Successful POST (include Location header)
204 No Content # Successful DELETE
客户端错误
400 Bad Request # Invalid request body/parameters
401 Unauthorized # Missing or invalid authentication
403 Forbidden # Authenticated but not authorized
404 Not Found # Resource doesn't exist
409 Conflict # Resource conflict (duplicate, etc.)
422 Unprocessable Entity# Validation errors
429 Too Many Requests # Rate limit exceeded
服务器错误
500 Internal Server Error # Unexpected server error
502 Bad Gateway # Upstream service error
503 Service Unavailable # Temporary overload
响应负载
单资源成功
{
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-01-15T10:30:00Z"
},
"meta": {
"request_id": "req_abc123"
}
}
带分页的集合响应
{
"data": [
{ "id": 1, "name": "Product A" },
{ "id": 2, "name": "Product B" }
],
"meta": {
"total": 150,
"page": 1,
"per_page": 20,
"total_pages": 8
},
"links": {
"self": "/products?page=1",
"next": "/products?page=2",
"last": "/products?page=8"
}
}
错误响应
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "age", "message": "Must be at least 18" }
]
},
"meta": {
"request_id": "req_xyz789"
}
}
提示: 始终在响应中包含
request_id(或类似字段),以便进行调试和支持。
Source: …
基于查询的特性
过滤
GET /products?category=electronics&brand=apple&min_price=100&max_price=500
排序
GET /products?sort=price:asc,created_at:desc
分页
-
基于偏移
GET /products?page=2&per_page=20 -
基于游标(更适用于大数据集)
GET /products?cursor=eyJpZCI6MTAwfQ&limit=20
稀疏字段集(字段选择)
GET /users/123?fields=id,name,email
包含关联资源
GET /orders/123?include=user,items,shipping_address
版本控制
GET /v1/users
GET /v2/users
基于 Header 的版本控制(推荐)
GET /users
Accept: application/vnd.myapi.v2+json
弃用 Header(针对旧版本)
{
"headers": {
"Deprecation": "Sun, 01 Jan 2025 00:00:00 GMT",
"Sunset": "Sun, 01 Jul 2025 00:00:00 GMT",
"Link": "; rel=\"successor-version\""
}
}
身份验证与授权
Bearer 令牌(Authorization 头)
GET /api/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
JWT 负载示例
{
"header": { "alg": "RS256", "typ": "JWT" },
"payload": {
"sub": "user_123",
"email": "user@example.com",
"roles": ["user", "admin"],
"iat": 1704067200,
"exp": 1704153600
}
}
API‑Key(头部 – 推荐)
GET /api/data
X-API-Key: sk_live_abc123xyz
API‑Key(查询参数 – 安全性较低)
GET /api/data?api_key=sk_live_abc123xyz
细粒度权限模型
read:users– 读取用户数据write:users– 创建/更新用户delete:users– 删除用户read:orders– 读取订单数据admin– 完全访问
包含作用域的令牌
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:users read:orders"
}
限流
成功响应头
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1704067200
当被限流时
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
滑动窗口实现(示例)
const rateLimiter = {
// Per‑user limits
authenticated: {
requests: 1000,
window: '1 hour'
},
// Per‑IP limits for unauthenticated callers
anonymous: {
requests: 100,
window: '1 hour'
},
// Per‑endpoint limits
endpoints: {
'POST /auth/login': { requests: 5, window: '1 minute' },
'POST /users': { requests: 10, window: '1 hour' }
}
};
结构化错误目录
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "The requested user was not found",
"details": {
"resource_type": "User",
"resource_id": "123"
},
"documentation_url": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND",
"request_id": "req_abc123"
}
}
集中式错误码映射(示例)
const errorCodes = {
// Authentication errors (1xxx)
INVALID_CREDENTIALS: { status: 401, message: 'Invalid email or password' },
TOKEN_EXPIRED: { status: 401, message: 'Authentication token has expired' },
INSUFFICIENT_PERMISSIONS: { status: 403, message: 'Insufficient permissions' },
// Validation errors (2xxx)
VALIDATION_ERROR: { status: 422, message: 'Validation failed' },
// Resource errors (3xxx)
RESOURCE_NOT_FOUND: { status: 404, message: 'Resource not found' },
CONFLICT: { status: 409, message: 'Resource conflict' },
// Rate‑limit errors (4xxx)
TOO_MANY_REQUESTS: { status: 429, message: 'Rate limit exceeded' },
// Server errors (5xxx)
INTERNAL_ERROR: { status: 500, message: 'Internal server error' }
};
快速检查清单
- Resources:名词,集合使用复数,单个项目使用单数 ID。
- Naming:URL 使用 kebab‑case,查询参数使用 snake_case。
- Methods:使用正确的 HTTP verb;在适当情况下保持幂等。
- Status codes:针对每种情况返回最具体的代码。
- Responses:在分页时包含
data、meta(包含request_id)和links。 - Filtering / Sorting / Pagination:通过查询参数公开。
- Versioning:URL 或 media‑type 版本控制;使用适当的 Header 进行废弃。
- Auth:优先使用基于 Header 的 token 或 API key;嵌入 scopes/roles。
- Rate limiting:通过响应 Header 传达限制;实现滑动窗口。
- Errors:保持一致的 JSON 结构、错误代码、文档链接和 request IDs。
遵循这些指南,构建 清晰、一致且易于使用 的 API。
错误定义
RMISSIONS: { status: 403, message: 'You do not have permission' },
// Validation errors (2xxx)
VALIDATION_ERROR: { status: 422, message: 'Request validation failed' },
INVALID_FORMAT: { status: 400, message: 'Invalid request format' },
// Resource errors (3xxx)
RESOURCE_NOT_FOUND: { status: 404, message: 'Resource not found' },
RESOURCE_CONFLICT: { status: 409, message: 'Resource already exists' },
// Rate limiting (4xxx)
RATE_LIMIT_EXCEEDED: { status: 429, message: 'Too many requests' },
// Server errors (5xxx)
INTERNAL_ERROR: { status: 500, message: 'An unexpected error occurred' },
SERVICE_UNAVAILABLE: { status: 503, message: 'Service temporarily unavailable' },
};
示例 HAL‑style 响应
{
"data": {
"id": 123,
"status": "pending",
"total": 99.99
},
"links": {
"self": { "href": "/orders/123" },
"customer": { "href": "/users/456" },
"items": { "href": "/orders/123/items" }
},
"actions": {
"cancel": {
"href": "/orders/123/cancel",
"method": "POST",
"title": "Cancel this order"
},
"pay": {
"href": "/orders/123/pay",
"method": "POST",
"title": "Process payment"
}
}
}
OpenAPI 规范 (YAML)
openapi: 3.0.3
info:
title: My API
version: 1.0.0
description: A sample API
paths:
/users:
get:
summary: List all users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
format: email
API 设计最佳实践
设计良好的 API 直观、一致且可扩展。通过遵循这些最佳实践——合理的资源命名、恰当的状态码、全面的错误处理以及清晰的文档,你可以打造让开发者乐于使用的 API。
关键要点
- 使用名词表示资源,使用 HTTP 方法表示操作。
- 返回恰当的状态码。
- 从一开始就实现适当的版本控制。
- 设计完整的错误响应。
- 使用 OpenAPI 对所有内容进行文档化。