RESTful API 设计原则
字数 2871 2025-11-07 22:15:37

RESTful API 设计原则

RESTful API 是一种基于 REST(Representational State Transfer,表述性状态转移)架构风格设计的 API。它不是一种具体的技术或协议,而是一组设计原则和约束条件的集合。理解并遵循这些原则,能帮助你设计出易于理解、使用、扩展和维护的 Web 服务接口。

核心概念:资源(Resource)
REST 的核心抽象是“资源”。资源可以是任何事物,比如一个用户、一篇订单、一件商品,甚至是一个抽象的概念(如一次交易会话)。每个资源都有一个唯一的标识符,即 URI(统一资源标识符)。例如:

  • /users/123 标识了 ID 为 123 的用户资源。
  • /orders/456 标识了 ID 为 456 的订单资源。

设计原则的循序渐进解析

第一步:统一接口(Uniform Interface)
这是 REST 最重要的约束,它包含以下几个关键点,确保了客户端和服务器之间通信方式的一致性。

  1. 资源的标识(Identification of Resources)

    • 描述:每个资源都通过一个唯一的 URI 来标识。客户端通过访问这个 URI 来与资源交互。
    • 示例:要获取用户 123 的信息,客户端应请求 GET /users/123,而不是 GET /getUser?id=123。URI 应该只表示资源本身,而不包含操作(动词)。
  2. 通过表述来操作资源(Manipulation of Resources through Representations)

    • 描述:客户端并不直接操作服务器上的资源,而是操作资源的“表述”(Representation)。当客户端获取或修改一个资源时,它实际上是在交换该资源的完整或部分表述(通常是 JSON 或 XML 格式)。
    • 示例:客户端通过 GET /users/123 收到一个用户资源的 JSON 表述。当它要更新这个用户时,它会将修改后的 JSON 表述通过 PUTPATCH 请求发送给服务器。
  3. 自描述的消息(Self-descriptive Messages)

    • 描述:每个请求和响应消息都包含足够的信息,说明如何处理自己。这主要通过 HTTP 动词和 HTTP 头部(Headers)来实现。
    • 示例:一个 POST 请求的 Content-Type 头设置为 application/json,告诉服务器“我发送的数据是 JSON 格式”。响应中的 Content-Type 和状态码(如 200 OK, 404 Not Found)则告诉客户端如何处理返回的内容。
  4. 超媒体作为应用状态引擎(HATEOAS)

    • 描述:这是 REST 中最成熟也常被忽略的原则。服务器的响应中不仅包含资源数据,还应包含指向相关资源的链接(超链接)。客户端通过跟随这些链接来驱动应用的状态变迁,而不需要硬编码 URI 结构。
    • 示例:获取订单资源的响应可能如下:
      {
        "id": 456,
        "total": 99.99,
        "status": "shipped",
        "_links": {
          "self": { "href": "/orders/456" },
          "customer": { "href": "/users/123" },
          "invoice": { "href": "/invoices/789" }
        }
      }
      
      客户端知道可以通过 customer 链接去获取下单用户的信息,而无需自己拼接 /users/123 这个 URI。

第二步:无状态(Stateless)

  • 描述:服务器不会在多个请求之间保存任何客户端的状态信息。每一个从客户端发到服务器的请求都必须包含理解该请求所需的全部信息。
  • 原理:会话状态(Session State)应该完全保存在客户端(例如,通过 Token 或 Cookie)。这使服务器更容易扩展,因为任何服务器实例都可以处理任何客户端的请求,而无需关心之前的交互历史。
  • 示例:用户的认证信息应该通过每个请求的 Authorization 头传递,而不是由服务器在内存中维护一个会话列表。

第三步:可缓存(Cacheable)

  • 描述:服务器应对响应明确标识其是否可被缓存,以及可以缓存多久。这可以显著减少客户端-服务器交互,提升性能。
  • 原理:利用 HTTP 协议内置的缓存机制,如 Cache-ControlExpiresETag 头部。
  • 示例:对于不常变化的资源(如商品分类列表),服务器可以在响应头中添加 Cache-Control: max-age=3600,告诉客户端和中间缓存(如 CDN)可以将此响应缓存 1 小时。

第四步:分层系统(Layered System)

  • 描述:系统的架构可以由多层组成。客户端无需知道它是直接与服务器通信,还是与中间的代理(如负载均衡器)、网关或缓存服务器通信。这有助于提高系统的可扩展性和安全性。
  • 原理:例如,可以在 API 服务器前部署负载均衡器来分发流量,部署 API 网关来处理认证、限流等公共逻辑,而客户端对此无感知。

第五步:按需代码(Code-On-Demand,可选)

  • 描述:服务器可以临时扩展或定制客户端的功能,比如通过向客户端发送可执行的代码(如 JavaScript 小程序)。这是 REST 约束中唯一可选的。
  • 示例:在 Web 浏览器中,服务器可以返回一段 JavaScript 代码来渲染一个复杂的 UI 组件。但在大多数 API 设计中(尤其是移动端/后端通信),此约束较少使用。

核心实现:HTTP 动词的正确使用
RESTful API 利用 HTTP 方法(动词)来定义对资源的操作,这是“统一接口”原则的关键实践。

HTTP 方法 操作 描述 典型 URI 示例
GET 检索/获取 安全且幂等的。用于获取资源的表述。 GET /users (获取列表), GET /users/123 (获取单个)
POST 创建 非幂等。用于创建新资源。服务器为新资源分配 URI。 POST /users (请求体中包含新用户数据)
PUT 替换/更新 幂等的。用于更新整个资源。客户端提供完整的更新后表述。 PUT /users/123 (请求体中包含用户 123 的全部新数据)
PATCH 部分更新 非幂等(应设计为幂等)。用于对资源进行部分修改。 PATCH /users/123 (请求体中只包含要修改的字段,如 {“email": "new@email.com"})
DELETE 删除 幂等的。用于删除指定资源。 DELETE /users/123

总结
设计一个优秀的 RESTful API,本质上是将你的业务模型映射为一组“资源”,然后通过标准的 HTTP 协议(URI、方法、状态码、头部)来对这些资源进行增删改查。遵循上述原则,特别是统一接口和无状态,能够创造出结构清晰、松耦合、易于扩展的 Web 服务。

RESTful API 设计原则 RESTful API 是一种基于 REST(Representational State Transfer,表述性状态转移)架构风格设计的 API。它不是一种具体的技术或协议,而是一组设计原则和约束条件的集合。理解并遵循这些原则,能帮助你设计出易于理解、使用、扩展和维护的 Web 服务接口。 核心概念:资源(Resource) REST 的核心抽象是“资源”。资源可以是任何事物,比如一个用户、一篇订单、一件商品,甚至是一个抽象的概念(如一次交易会话)。每个资源都有一个唯一的标识符,即 URI(统一资源标识符)。例如: /users/123 标识了 ID 为 123 的用户资源。 /orders/456 标识了 ID 为 456 的订单资源。 设计原则的循序渐进解析 第一步:统一接口(Uniform Interface) 这是 REST 最重要的约束,它包含以下几个关键点,确保了客户端和服务器之间通信方式的一致性。 资源的标识(Identification of Resources) 描述 :每个资源都通过一个唯一的 URI 来标识。客户端通过访问这个 URI 来与资源交互。 示例 :要获取用户 123 的信息,客户端应请求 GET /users/123 ,而不是 GET /getUser?id=123 。URI 应该只表示资源本身,而不包含操作(动词)。 通过表述来操作资源(Manipulation of Resources through Representations) 描述 :客户端并不直接操作服务器上的资源,而是操作资源的“表述”(Representation)。当客户端获取或修改一个资源时,它实际上是在交换该资源的完整或部分表述(通常是 JSON 或 XML 格式)。 示例 :客户端通过 GET /users/123 收到一个用户资源的 JSON 表述。当它要更新这个用户时,它会将修改后的 JSON 表述通过 PUT 或 PATCH 请求发送给服务器。 自描述的消息(Self-descriptive Messages) 描述 :每个请求和响应消息都包含足够的信息,说明如何处理自己。这主要通过 HTTP 动词和 HTTP 头部(Headers)来实现。 示例 :一个 POST 请求的 Content-Type 头设置为 application/json ,告诉服务器“我发送的数据是 JSON 格式”。响应中的 Content-Type 和状态码(如 200 OK , 404 Not Found )则告诉客户端如何处理返回的内容。 超媒体作为应用状态引擎(HATEOAS) 描述 :这是 REST 中最成熟也常被忽略的原则。服务器的响应中不仅包含资源数据,还应包含指向相关资源的链接(超链接)。客户端通过跟随这些链接来驱动应用的状态变迁,而不需要硬编码 URI 结构。 示例 :获取订单资源的响应可能如下: 客户端知道可以通过 customer 链接去获取下单用户的信息,而无需自己拼接 /users/123 这个 URI。 第二步:无状态(Stateless) 描述 :服务器不会在多个请求之间保存任何客户端的状态信息。每一个从客户端发到服务器的请求都必须包含理解该请求所需的全部信息。 原理 :会话状态(Session State)应该完全保存在客户端(例如,通过 Token 或 Cookie)。这使服务器更容易扩展,因为任何服务器实例都可以处理任何客户端的请求,而无需关心之前的交互历史。 示例 :用户的认证信息应该通过每个请求的 Authorization 头传递,而不是由服务器在内存中维护一个会话列表。 第三步:可缓存(Cacheable) 描述 :服务器应对响应明确标识其是否可被缓存,以及可以缓存多久。这可以显著减少客户端-服务器交互,提升性能。 原理 :利用 HTTP 协议内置的缓存机制,如 Cache-Control 、 Expires 和 ETag 头部。 示例 :对于不常变化的资源(如商品分类列表),服务器可以在响应头中添加 Cache-Control: max-age=3600 ,告诉客户端和中间缓存(如 CDN)可以将此响应缓存 1 小时。 第四步:分层系统(Layered System) 描述 :系统的架构可以由多层组成。客户端无需知道它是直接与服务器通信,还是与中间的代理(如负载均衡器)、网关或缓存服务器通信。这有助于提高系统的可扩展性和安全性。 原理 :例如,可以在 API 服务器前部署负载均衡器来分发流量,部署 API 网关来处理认证、限流等公共逻辑,而客户端对此无感知。 第五步:按需代码(Code-On-Demand,可选) 描述 :服务器可以临时扩展或定制客户端的功能,比如通过向客户端发送可执行的代码(如 JavaScript 小程序)。这是 REST 约束中唯一可选的。 示例 :在 Web 浏览器中,服务器可以返回一段 JavaScript 代码来渲染一个复杂的 UI 组件。但在大多数 API 设计中(尤其是移动端/后端通信),此约束较少使用。 核心实现:HTTP 动词的正确使用 RESTful API 利用 HTTP 方法(动词)来定义对资源的操作,这是“统一接口”原则的关键实践。 | HTTP 方法 | 操作 | 描述 | 典型 URI 示例 | | :--- | :--- | :--- | :--- | | GET | 检索/获取 | 安全且幂等的。用于获取资源的表述。 | GET /users (获取列表), GET /users/123 (获取单个) | | POST | 创建 | 非幂等。用于创建新资源。服务器为新资源分配 URI。 | POST /users (请求体中包含新用户数据) | | PUT | 替换/更新 | 幂等的。用于更新 整个 资源。客户端提供完整的更新后表述。 | PUT /users/123 (请求体中包含用户 123 的全部新数据) | | PATCH | 部分更新 | 非幂等(应设计为幂等)。用于对资源进行 部分 修改。 | PATCH /users/123 (请求体中只包含要修改的字段,如 {“email": "new@email.com"} ) | | DELETE | 删除 | 幂等的。用于删除指定资源。 | DELETE /users/123 | 总结 设计一个优秀的 RESTful API,本质上是将你的业务模型映射为一组“资源”,然后通过标准的 HTTP 协议(URI、方法、状态码、头部)来对这些资源进行增删改查。遵循上述原则,特别是统一接口和无状态,能够创造出结构清晰、松耦合、易于扩展的 Web 服务。