REST 架构风格

2000 年,Roy Fielding 在其博士论文中提出了 表现层状态转换(Representational State Transfer,REST)。作为一种分布式超媒体系统的架构风格,REST 并非一种协议或标准,而是一组设计原则。其中最核心、也最容易被忽视的一条约束是 HATEOAS(Hypermedia As The Engine Of Application State)——超媒体作为应用状态的引擎。

HATEOAS:REST 的灵魂

HATEOAS 要求服务端返回的响应中包含指向相关操作的链接,客户端通过这些链接发现和执行下一步操作,而非事先约定好 URL 结构。理想情况下,客户端只需要知道一个入口 URL,其他所有交互路径都由服务端通过超媒体动态提供。

{
  "order": {
    "id": "12345",
    "status": "pending",
    "total": 99.99,
    "_links": {
      "self": "/orders/12345",
      "cancel": "/orders/12345/cancel",
      "payment": "/orders/12345/payment",
      "history": "/orders/12345/history"
    }
  }
}

这个理念早在 Web 诞生之初就深植于 HTML 之中——网页上的链接驱动着用户在网站中的导航。但大多数所谓的 "RESTful API" 只是把 JSON 当作传输格式,客户端需要事先了解所有端点结构,本质上与 RPC 并无区别。这正是 Fielding 所批评的「REST 不是 RPC」的真正含义。

RESTful API 设计规范

尽管 HATEOAS 很少被完整实现,业界仍然形成了一套实用的 RESTful API 设计规范。

资源命名遵循名词复数形式,而非动词:

# 正确
GET /users/123
POST /users
DELETE /users/123

# 错误:动词不应出现在路径中
GET /getUser/123
POST /createUser

HTTP 动词对应 CRUD 操作:

动词语义幂等性安全性
GET读取资源幂等安全
POST创建资源非幂等不安全
PUT完整更新幂等不安全
PATCH部分更新非幂等不安全
DELETE删除资源幂等不安全

状态码必须准确表达响应含义,而不是用 200 或 500 敷衍了事:

200 OK           # 成功
201 Created      # 资源创建成功,响应头包含 Location
204 No Content   # 成功但无返回体(如 DELETE)
400 Bad Request  # 请求格式错误
401 Unauthorized # 未认证
403 Forbidden    # 已认证但无权限
404 Not Found    # 资源不存在
409 Conflict    # 资源状态冲突(如重复创建)
422 Unprocessable Entity  # 语义错误(参数校验失败)
429 Too Many Requests # 限流
500 Internal Server Error # 服务端错误

常见反模式

REST 的普及伴随着大量反模式的涌现。

版本号放在 URL 中是最常见的误区。/v1/users/v2/users 意味着客户端需要管理多个版本的接口逻辑。RESTful 的版本管理应该通过内容协商(Content Negotiation)实现:

GET /users/123
Accept: application/vnd.myapi.v2+json

过度嵌套的 URL 会导致脆弱的客户端代码。/users/123/orders/456/items/789 这样的路径意味着客户端必须知道完整的资源层级关系。更好的做法是让每个资源都可以独立寻址:

# 扁平化设计
GET /items/789

响应结构不一致是另一个常见问题。成功时返回数据,失败时返回错误消息的格式因人而异。应该定义统一的错误响应结构:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数校验失败",
    "details": [
      { "field": "email", "issue": "格式不正确" },
      { "field": "age", "issue": "必须为正整数" }
    ],
    "traceId": "abc123"
  }
}

与 GraphQL、gRPC 的竞争关系

REST、GraphQL 和 gRPC 分别代表了 API 设计的三种哲学。

REST 的优势在于简单性和 HTTP 原生支持:不需要特殊的客户端库,可以直接在浏览器和任何 HTTP 工具中测试。缓存机制成熟,与 CDN 天然兼容。但过度获取(Over-fetching)和获取不足(Under-fetching)问题在实际使用中确实存在。

GraphQL 将数据选择权交给客户端,适合数据需求复杂、多个前端需要不同数据视图的场景。但它增加了架构复杂度,需要额外的 Schema 设计和 DataLoader 实现。

gRPC 基于 Protocol Buffers,提供高效的二进制序列化和完整的 RPC 语义,适合服务间通信和强类型契约的场景。但不适合浏览器直接调用,在 API 公开化方面不如 REST。

这三种方案并非互斥,很多大型系统会组合使用:面向外部合作伙伴的公开 API 使用 REST,内部服务间通信使用 gRPC,数据密集型的 BFF 层使用 GraphQL。