跳到主要内容

REST API 设计规范

设计一个优秀的REST API需要遵循一系列规范和最佳实践。本章将详细介绍URL设计、HTTP方法使用、状态码规范以及错误处理等内容。

URL设计原则

URL是资源的唯一标识符,良好的URL设计是REST API的基础。

使用名词表示资源

URL应该表示资源,而不是操作。资源使用名词表示,操作通过HTTP方法体现。

正确示例

GET    /users          获取用户列表
POST /users 创建新用户
GET /users/123 获取ID为123的用户
PUT /users/123 更新ID为123的用户
DELETE /users/123 删除ID为123的用户

错误示例

GET    /getUsers       ❌ URL中包含动词
POST /createUser ❌ 操作应该在HTTP方法中体现
DELETE /deleteUser/123 ❌ 同上

使用复数形式

资源名称应该使用复数形式,这样语义更清晰,也便于扩展。

正确示例

/users          用户集合
/users/123 单个用户
/articles 文章集合
/articles/456 单篇文章

错误示例

/user           ❌ 单数形式
/article/456 ❌ 单数形式

表达资源层级关系

当资源之间存在层级关系时,可以通过URL路径表达这种关系。

/users/123/orders              用户123的所有订单
/users/123/orders/456 用户123的订单456
/articles/456/comments 文章456的所有评论
/articles/456/comments/789 文章456的评论789

层级关系的设计原则:

  • 层级不宜过深,一般不超过3层
  • 如果子资源可以独立存在,考虑使用单独的URL
  • 通过查询参数过滤,而不是增加层级

使用连字符提高可读性

当资源名称包含多个单词时,使用连字符(-)连接,不要使用下划线或驼峰命名。

正确示例

/blog-posts
/user-profiles
/order-items

错误示例

/blogPosts     ❌ 驼峰命名
/blog_posts ❌ 下划线
/blogposts ❌ 单词连在一起

使用小写字母

URL应该全部使用小写字母,因为URL是大小写敏感的,使用小写可以避免混淆。

正确示例

/users
/blog-posts
/api-docs

错误示例

/Users         ❌ 大写字母
/BlogPosts ❌ 大写字母

避免尾部斜杠

URL末尾不应该有斜杠,这会导致歧义和重复。

正确示例

/users
/articles/123

错误示例

/users/        ❌ 尾部斜杠
/articles/123/ ❌ 尾部斜杠

使用查询参数过滤和分页

对于列表资源的过滤、排序、分页,应该使用查询参数而不是URL路径。

过滤:
GET /articles?status=published&author=123

排序:
GET /articles?sort=createdAt&order=desc

分页:
GET /articles?page=2&limit=20

搜索:
GET /articles?keyword=REST

组合使用:
GET /articles?status=published&sort=createdAt&order=desc&page=1&limit=10

版本控制

API版本应该放在URL中,通常使用v前缀。

/api/v1/users
/api/v2/users

版本号的位置选择:

  • 放在域名后:https://api.example.com/v1/users
  • 放在路径前:https://example.com/api/v1/users

动作资源的设计

有些操作难以用标准HTTP方法表达,可以使用"动作资源"模式。

POST /articles/123/publish      发布文章
POST /orders/456/cancel 取消订单
POST /users/123/follow 关注用户

这些URL表示的是一种"动作资源",POST请求创建这个动作资源,从而触发相应的业务逻辑。

HTTP方法规范

HTTP方法定义了对资源的操作类型,正确使用HTTP方法是REST API设计的关键。

常用HTTP方法

方法用途是否幂等是否安全
GET获取资源
POST创建资源
PUT完整更新资源
PATCH部分更新资源
DELETE删除资源

幂等性和安全性

安全性:安全方法不会修改服务器上的资源状态。GET、HEAD、OPTIONS是安全方法。

幂等性:幂等方法执行一次和执行多次的效果相同。GET、PUT、DELETE、HEAD、OPTIONS是幂等方法。

理解这两个概念对于设计可靠的API非常重要:

// GET是安全和幂等的
GET /users/123
// 无论执行多少次,服务器状态不变,返回结果相同

// PUT是幂等的
PUT /users/123
{ "name": "张三" }
// 执行一次和执行多次,用户123的名字都是"张三"

// POST不是幂等的
POST /orders
{ "userId": 123, "amount": 100 }
// 每次执行都会创建一个新订单!

GET方法

GET方法用于获取资源,是最常用的HTTP方法。

获取单个资源

GET /users/123 HTTP/1.1
Host: api.example.com
Accept: application/json

获取资源列表

GET /users?page=1&limit=20 HTTP/1.1
Host: api.example.com
Accept: application/json

GET请求的注意事项:

  • 不应该在请求体中发送数据
  • 可以被缓存
  • 参数在URL中传递
  • 不应该修改服务器状态

POST方法

POST方法用于创建新资源。

POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
"name": "张三",
"email": "[email protected]"
}

响应应该包含:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /users/124

{
"id": 124,
"name": "张三",
"email": "[email protected]",
"createdAt": "2024-10-21T10:30:00Z"
}

POST也常用于执行复杂操作:

POST /orders/456/cancel HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
"reason": "用户主动取消"
}

PUT方法

PUT方法用于完整更新资源,需要提供资源的所有字段。

PUT /users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
"name": "李四",
"email": "[email protected]",
"age": 25
}

PUT的特点:

  • 必须提供完整资源数据
  • 如果资源不存在,通常会创建新资源
  • 是幂等操作

PATCH方法

PATCH方法用于部分更新资源,只需要提供要修改的字段。

PATCH /users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
"email": "[email protected]"
}

PATCH的特点:

  • 只修改提供的字段
  • 其他字段保持不变
  • 不是幂等操作(取决于实现)

DELETE方法

DELETE方法用于删除资源。

DELETE /users/123 HTTP/1.1
Host: api.example.com

响应可以是:

HTTP/1.1 204 No Content

或者返回被删除的资源:

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": 123,
"deleted": true
}

HTTP状态码规范

HTTP状态码是API响应的重要组成部分,正确使用状态码可以帮助客户端理解请求结果。

状态码分类

类别含义
1xx信息性响应
2xx成功
3xx重定向
4xx客户端错误
5xx服务器错误

常用状态码详解

2xx 成功状态码

200 OK:请求成功,用于GET、PUT、PATCH、DELETE。

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": 123,
"name": "张三"
}

201 Created:资源创建成功,用于POST。

HTTP/1.1 201 Created
Location: /users/124
Content-Type: application/json

{
"id": 124,
"name": "张三"
}

204 No Content:请求成功,无返回内容。常用于DELETE或PUT。

HTTP/1.1 204 No Content

202 Accepted:请求已接受,但处理尚未完成。用于异步操作。

HTTP/1.1 202 Accepted
Content-Type: application/json

{
"taskId": "abc123",
"status": "processing",
"estimatedTime": "5 minutes"
}

3xx 重定向状态码

301 Moved Permanently:资源已永久移动到新位置。

HTTP/1.1 301 Moved Permanently
Location: /api/v2/users/123

304 Not Modified:资源未修改,使用缓存版本。配合ETag或Last-Modified使用。

HTTP/1.1 304 Not Modified
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

4xx 客户端错误状态码

400 Bad Request:请求格式错误或参数无效。

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
"error": "Invalid request",
"message": "Email format is invalid",
"field": "email"
}

401 Unauthorized:未认证,需要登录。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

{
"error": "Unauthorized",
"message": "Authentication required"
}

403 Forbidden:已认证但无权限访问。

HTTP/1.1 403 Forbidden
Content-Type: application/json

{
"error": "Forbidden",
"message": "You do not have permission to access this resource"
}

404 Not Found:资源不存在。

HTTP/1.1 404 Not Found
Content-Type: application/json

{
"error": "Not Found",
"message": "User with id 999 not found"
}

405 Method Not Allowed:请求方法不允许。

HTTP/1.1 405 Method Not Allowed
Allow: GET, POST, PUT
Content-Type: application/json

{
"error": "Method Not Allowed",
"message": "DELETE method is not supported for this resource"
}

409 Conflict:请求与服务器状态冲突。

HTTP/1.1 409 Conflict
Content-Type: application/json

{
"error": "Conflict",
"message": "Email already exists",
"field": "email"
}

422 Unprocessable Entity:请求格式正确但语义错误。

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
"error": "Validation Failed",
"errors": [
{
"field": "password",
"message": "Password must be at least 8 characters"
},
{
"field": "age",
"message": "Age must be a positive number"
}
]
}

429 Too Many Requests:请求过于频繁,触发限流。

HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json

{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Please retry after 60 seconds"
}

5xx 服务器错误状态码

500 Internal Server Error:服务器内部错误。

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
"error": "Internal Server Error",
"message": "An unexpected error occurred",
"requestId": "req-abc123"
}

502 Bad Gateway:网关或代理服务器收到无效响应。

503 Service Unavailable:服务暂时不可用。

HTTP/1.1 503 Service Unavailable
Retry-After: 300
Content-Type: application/json

{
"error": "Service Unavailable",
"message": "Service is temporarily unavailable for maintenance"
}

504 Gateway Timeout:网关或代理服务器等待上游响应超时。

错误处理规范

良好的错误处理可以帮助开发者快速定位问题,提高开发效率。

错误响应格式

统一的错误响应格式应该包含以下信息:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求参数验证失败",
"details": [
{
"field": "email",
"message": "邮箱格式不正确"
},
{
"field": "password",
"message": "密码长度至少8位"
}
],
"requestId": "req-abc123",
"timestamp": "2024-10-21T10:30:00Z",
"documentation": "https://api.example.com/docs/errors/VALIDATION_ERROR"
}
}

错误码设计

错误码应该具有描述性,便于开发者理解和搜索:

VALIDATION_ERROR     参数验证错误
AUTH_REQUIRED 需要认证
AUTH_EXPIRED 认证已过期
PERMISSION_DENIED 权限不足
RESOURCE_NOT_FOUND 资源不存在
RESOURCE_CONFLICT 资源冲突
RATE_LIMITED 请求频率超限
INTERNAL_ERROR 内部错误

分页设计

列表接口应该支持分页,避免返回过多数据。

请求参数

GET /articles?page=1&limit=20

响应格式

{
"data": [
{ "id": 1, "title": "文章1" },
{ "id": 2, "title": "文章2" }
],
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"totalPages": 5
},
"links": {
"first": "/articles?page=1&limit=20",
"prev": null,
"next": "/articles?page=2&limit=20",
"last": "/articles?page=5&limit=20"
}
}

过滤、排序、字段选择

过滤

GET /articles?status=published&author=123

排序

GET /articles?sort=createdAt:desc,title:asc

字段选择

GET /articles?fields=id,title,author.name

请求和响应格式

请求格式

Content-Type:指定请求体的格式。

POST /users HTTP/1.1
Content-Type: application/json

{
"name": "张三",
"email": "[email protected]"
}

Accept:指定期望的响应格式。

GET /users/123 HTTP/1.1
Accept: application/json

响应格式

响应应该包含适当的头部信息:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
X-Request-Id: req-abc123
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999

{
"id": 123,
"name": "张三"
}

总结

设计优秀的REST API需要遵循一系列规范:

  1. URL设计:使用名词、复数形式、小写字母、连字符
  2. HTTP方法:正确使用GET、POST、PUT、PATCH、DELETE
  3. 状态码:使用合适的状态码表达请求结果
  4. 错误处理:提供清晰、一致的错误信息
  5. 分页和过滤:支持分页、过滤、排序等查询功能

遵循这些规范,可以设计出易用、一致、可维护的REST API。