2026-08-21 14:47:46.324577+08 / restful-api

Resources

围绕业务资源建模、资源命名、层级设计和标识符设计的 RESTful API 指南。

Restful ApiApi DesignOpenapi

Resources

对业务领域进行建模

API 应对业务领域进行建模,并暴露能够表达有意义业务概念的资源。

资源应包含 API 消费者所需的必要信息,同时避免暴露内部实现细节。

良好的资源模型可以带来:

  • 对业务领域更清晰的理解
  • 一致的资源表示方式
  • 降低客户端与服务之间的耦合
  • 更好的 API 演进能力

API 不应只是数据库表的简单包装。业务概念和领域逻辑应通过有意义的资源表达,而不是直接暴露持久化模型。

对于复杂领域,可以根据不同业务上下文使用不同模型。应始终清晰定义模型适用的上下文。


定义有用的资源

资源应覆盖大多数客户端使用场景。

有用的资源应:

  • 为常见场景提供足够的信息
  • 避免暴露不必要的数据
  • 通过过滤、分页或嵌入机制提供灵活性

目标是提供平衡的资源表示:

  • 信息太少,会迫使客户端发起不必要的 API 调用。
  • 信息太多,会增加负载大小和耦合程度。

避免在资源 URL 中使用动作

REST API 应以资源为中心。

动作应通过 HTTP 方法表达,而不是在 URL 中使用动词。

使用:

http
POST /orders
GET /orders/{id}
PUT /orders/{id}
DELETE /orders/{id}

避免:

http
POST /orders/create
POST /orders/{id}/cancel
POST /orders/{id}/approve

URL 应表示资源,HTTP 方法应表示操作。

将动作建模为资源

当一个操作代表业务事件或状态转换时,可以将其建模为资源。

例如,不使用:

http
POST /vehicles/{vehicleId}/lock

可以使用:

http
PUT /vehicles/{vehicleId}/doors-lock

{
  "lockStatus": "locked"
}

或者:

http
POST /vehicles/{vehicleId}/door-actions

{
  "type": "unlock"
}

这种方式具有更好的扩展性,因为动作未来可以成为持久化资源,并支持:

  • 时间戳
  • 审计信息
  • 历史记录查询
  • 额外元数据

将复杂操作建模为资源

对于需要多个参数的复杂操作,应创建专用资源。

例如,不使用:

http
POST /vehicles/move

可以使用:

http
POST /vehicle-transfers

{
  "sourceOrganizationId": "org-a",
  "targetOrganizationId": "org-b",
  "vehicles": [
    "vehicle-1",
    "vehicle-2"
  ]
}

这样会创建一个表示转移操作的资源,并支持:

  • 跟踪操作状态
  • 审计历史操作
  • 支持异步处理

使用领域专属的资源名称

资源名称应表达业务概念。

推荐:

text
/sales-orders
/customers
/payment-transactions

避免使用泛化名称:

text
/items
/data
/entities
/objects

领域专属名称可以改善:

  • API 可读性
  • 开发者理解程度
  • 文档质量

使用适合 URL 的资源标识符

URL 中使用的资源标识符应只包含 URL 安全字符。

推荐字符:

text
A-Z
a-z
0-9
-
_
:
.

应避免使用需要额外编码的标识符。

示例:

推荐:

text
/customers/customer_123

避免:

text
/customers/customer name with spaces

通过路径段标识资源和子资源

资源和子资源应通过 URL 路径段表示。

通用结构:

text
/resources/{resource-id}/sub-resources/{sub-resource-id}

示例:

text
/customers/123
/customers/123/addresses
/customers/123/addresses/home

有效的资源路径通常意味着父资源也存在。

例如:

text
/customers/123/addresses/home

应隐含以下资源路径:

text
/customers
/customers/123
/customers/123/addresses

复合标识符不应使用 / 作为分隔符。


只在必要时使用 UUID

在分布式系统中,如果标识符必须由多个系统独立生成,UUID 会很有用。

但是 UUID 也有以下缺点:

  • 人类难以阅读和记忆
  • 调试不够方便
  • 存储和网络开销更大
  • 没有自然顺序
  • 不太适合作为面向用户的标识符

以下情况可以优先使用 UUID:

  • 需要大规模分布式 ID 生成
  • 多个系统需要独立生成 ID
  • 避免标识符冲突非常重要

对于简单的业务实体,可以优先使用服务端生成的标识符。

无论使用哪种标识符:

  • ID 都应表示为字符串
  • ID 创建后应保持稳定
  • ID 不应被重新分配给其他对象

限制资源类型数量

API 应避免暴露过多彼此无关的资源类型。

资源类型表示一组关系紧密的资源。

示例:

text
/customers
/customers/{id}
/customers/{id}/addresses
/addresses

设计良好的 API 通常只包含有限数量的资源类型。

如果 API 暴露了大量互不相关的资源,应考虑根据业务领域或限界上下文拆分 API。


限制子资源嵌套层级

只有当子资源的生命周期与父资源高度相关时,才应使用嵌套资源。

推荐:

text
/customers/{id}/orders/{orderId}

避免深层嵌套:

text
/customers/{id}/orders/{orderId}/items/{itemId}/details/{detailId}

深层嵌套会导致:

  • URL 复杂
  • API 不易发现
  • 耦合增加

通常建议将嵌套深度限制在约三层以内。


总结

良好的 REST API 应:

  • 对业务概念建模,而不是暴露数据库结构
  • 在 URL 中使用名词,而不是动词
  • 将复杂操作表示为资源
  • 使用有意义的领域专属资源名称
  • 保持标识符适合 URL 使用
  • 只在需要时使用 UUID
  • 避免资源类型过多
  • 避免资源深层嵌套

目标是创建可预测、易理解、可扩展且易于客户端使用的 API。

Resources | Remi Resume