网站Logo 青山黛

什么是Restful

1397628709
10
2025-09-01

RESTful 详细解析:概念、原则与实践

RESTful(Representational State Transfer,表述性状态转移)是一种基于 HTTP 协议设计分布式系统 API 的架构风格,而非严格的标准。它强调资源导向无状态交互统一接口,旨在提高系统的可扩展性、可读性和可维护性。目前,RESTful 已成为设计 Web API(如移动端后端、第三方服务接口)的主流范式。

一、RESTful 的核心概念

要理解 RESTful,首先需要明确其核心术语,这些术语是设计 API 的基础:

术语

定义

示例

资源(Resource)

API 操作的核心实体,可是具体数据(如用户、订单)或抽象概念(如权限、日志)。每个资源需有唯一标识。

用户(/users)、订单(/orders/123

资源标识(URI)

统一资源标识符,用于唯一定位一个或一组资源。RESTful 要求 URI 仅表示“资源是什么”,不包含“操作”(如 add/delete)。

/users(所有用户)、/users/456(ID 为 456 的用户)

表述(Representation)

资源的“表现形式”,即 API 传输的数据格式。资源本身是抽象的,表述是具体的。

JSON 格式的用户数据({"id":456,"name":"Alice"})、XML 格式的订单信息

状态转移(State Transfer)

客户端通过 HTTP 方法(如 GET/POST)触发资源状态的变化,或获取资源的当前状态。交互过程中,客户端状态由自身维护,服务器仅提供资源表述。

客户端发送 PUT /users/456 修改用户信息(资源状态转移)、发送 GET /users/456 获取用户当前状态

无状态(Stateless)

服务器不存储客户端的任何会话信息(如登录状态、操作历史),每个请求必须包含所有必要信息(如 Token),服务器仅根据请求本身完成处理。

客户端每次请求需携带 JWT Token,服务器无需保存“用户是否已登录”的状态

二、RESTful API 的 6 大核心原则

REST 架构的提出者 Roy Fielding 在其博士论文中定义了 6 个约束条件,满足这些条件的 API 才可称为“RESTful”:

1. 客户端-服务器(Client-Server)

  • 分离关注点:客户端负责“用户界面交互”(如展示数据、收集用户输入),服务器负责“数据存储与业务逻辑”。

  • 优势:客户端和服务器可独立开发、升级(如客户端从 Web 改为 App,服务器无需修改核心逻辑)。

2. 无状态(Stateless)

  • 核心要求:服务器不保存客户端的会话状态(如登录状态、浏览历史),每个请求必须包含所有必要信息(如身份凭证、资源标识)。

  • 优势

  • 服务器可水平扩展(新增服务器无需同步会话,直接接收请求);

  • 降低服务器复杂度(无需维护会话存储);

  • 提高容错性(某台服务器故障,客户端切换到其他服务器即可)。

  • 示例:客户端每次请求需携带 Authorization: Bearer <token>,服务器通过 Token 验证身份,不存储“用户会话”。

3. 缓存(Cacheable)

  • 核心要求:服务器需明确告知客户端“响应是否可缓存”(通过 HTTP 头如 Cache-ControlETag 实现),客户端可缓存可重用的响应,减少重复请求。

  • 优势:降低服务器负载,减少网络延迟,提升客户端体验。

  • 示例

  • 服务器返回静态资源(如用户头像)时,添加 Cache-Control: public, max-age=86400(客户端可缓存 1 天);

  • 动态资源(如用户余额)添加 Cache-Control: no-cache(客户端需验证响应是否最新,再决定是否使用缓存)。

4. 统一接口(Uniform Interface)

这是 RESTful 最核心的约束,定义了客户端与服务器的交互规则,确保接口的一致性。它包含 4 个子约束:

(1)资源识别(Identification of Resources)

  • 通过 URI 唯一识别资源,URI 仅描述“资源”,不包含“操作”。

  • 错误示例:/addUser(URI 包含操作)、/getOrder?id=123(参数冗余,应改为 /orders/123);

  • 正确示例:/users(所有用户)、/orders/123(ID 123 的订单)、/users/456/orders(用户 456 的所有订单)。

(2)通过表述操作资源(Manipulation of Resources Through Representations)

  • 客户端通过发送“资源表述”(如 JSON/XML)来修改资源状态。服务器接收表述后,更新对应的资源。

  • 示例:客户端发送 PUT /users/456 请求,携带 JSON 表述 {"name":"Alice","age":30},服务器根据该表述更新 ID 456 的用户信息。

(3)自描述消息(Self-Descriptive Messages)

  • 每个请求/响应需包含“如何处理该消息”的元数据(通过 HTTP 头实现),无需客户端与服务器预先约定额外规则。

  • 关键 HTTP 头:

  • Content-Type:告知接收方消息体的格式(如 application/jsonapplication/xml);

  • Accept:客户端告知服务器“期望接收的表述格式”(如 Accept: application/json);

  • HTTP 方法:表示请求的操作类型(如 GET 表示查询,POST 表示创建)。

(4)超媒体作为应用状态引擎(HATEOAS)

  • 核心思想:服务器返回的响应中,应包含“下一步可执行的操作链接”,客户端无需硬编码 URI,只需跟随链接即可完成交互(类似网页中的“超链接”)。

  • 优势:降低客户端与服务器的耦合(服务器修改 URI 时,客户端无需更新),提高 API 可发现性。

  • 示例:获取用户信息的响应中,包含“查看该用户订单”的链接:

{
  "id": 456,
  "name": "Alice",
  "links": [
    {
      "rel": "orders", 
      "href": "/users/456/orders", 
      "method": "GET"
    }
  ]
}

rel 表示链接含义,href 是目标 URI,method 是推荐 HTTP 方法)

5. 分层系统(Layered System)

  • 核心要求:系统可分为多个层级(如负载均衡层、缓存层、业务逻辑层、数据层),客户端仅与“最外层”交互,无需了解内层结构。

  • 优势

  • 提高系统安全性(如通过负载均衡层屏蔽内部服务器地址);

  • 便于扩展(如新增缓存层,客户端无需修改)。

6. 按需代码(Code on Demand,可选)

  • 核心要求:服务器可向客户端发送可执行代码(如 JavaScript、Applet),扩展客户端功能(如动态生成表单)。

  • 注意:这是唯一可选的约束,多数 RESTful API 不依赖此特性(避免安全风险和客户端复杂度)。

三、RESTful API 的设计实践

基于上述原则,RESTful API 设计需遵循以下具体规范:

1. URI 设计:资源为核心,简洁统一

  • 使用名词复数表示资源集合:如 /users(所有用户)、/orders(所有订单),而非 /user/order(单数易误解为“单个资源”)。

  • 使用嵌套表示资源关联:如 /users/456/orders(用户 456 的所有订单)、/orders/123/products(订单 123 包含的商品)。

  • 避免冗余词汇:URI 无需包含 api(如 /api/users 可简化为 /users,通过域名区分 API 服务,如 api.example.com/users)。

  • 使用连字符(-)分隔多单词资源:如 /user-profiles(用户资料),而非下划线(_)或驼峰式(userProfiles),符合 URI 命名习惯。

  • 禁止包含操作动词:URI 仅描述“资源”,操作通过 HTTP 方法表达(如不用 /deleteUser/456,而用 DELETE /users/456)。

2. HTTP 方法:映射资源操作

HTTP 方法(谓词)天然对应资源的 CRUD 操作,RESTful 要求严格遵循其语义:

HTTP 方法

核心语义

幂等性*

安全性**

适用场景

示例

GET

读取资源(查询)

获取单个/多个资源

GET /users(所有用户)、GET /users/456(单个用户)

POST

创建资源

新增资源(如创建用户、提交订单)

POST /users(创建用户,请求体含用户信息)

PUT

全量更新资源

替换资源的全部字段(需提供完整资源信息)

PUT /users/456(更新用户,请求体含所有字段)

PATCH

部分更新资源

仅更新资源的部分字段(无需提供完整信息)

PATCH /users/456(仅更新用户年龄:{"age":31}

DELETE

删除资源

删除指定资源

DELETE /users/456(删除用户 456)

*幂等性:多次执行相同请求,结果与执行一次相同(如 PUT /users/456 执行 1 次或 10 次,最终用户信息一致);
**安全性:请求不会修改服务器资源(仅读取,如 GET)。

3. 状态码:明确响应结果

使用标准 HTTP 状态码告知客户端请求的处理结果,避免自定义状态码(如 200 OK 配合 {"code":1001}),提高可读性:

状态码范围

类别

常用状态码

含义

示例

2xx

成功

200 OK

请求成功(GET/PUT/PATCH/DELETE)

GET /users/456 成功返回用户信息

201 Created

资源创建成功(POST)

POST /users 成功创建用户,响应头含 Location: /users/456

204 No Content

请求成功,但无响应体(如 DELETE)

DELETE /users/456 成功,无返回数据

4xx

客户端错误

400 Bad Request

请求参数错误(如缺少必填字段)

POST /users 未传 name 字段

401 Unauthorized

未认证(如未登录、Token 无效)

请求未携带 Token 或 Token 过期

403 Forbidden

已认证,但无权限(如普通用户删除管理员)

DELETE /users/1(普通用户操作)

404 Not Found

资源不存在

GET /users/999(ID 999 的用户不存在)

5xx

服务器错误

500 Internal Server Error

服务器内部错误(如代码异常)

业务逻辑报错、数据库连接失败

503 Service Unavailable

服务器暂时不可用(如维护、过载)

服务器重启中、请求量超过阈值

4. 数据交互:统一格式与细节

  • 优先使用 JSON 作为数据格式:JSON 轻量、易解析,支持多语言,是 RESTful API 的主流选择(避免 XML 的冗余)。

  • 请求体规范

  • 创建/更新资源时,请求体仅包含“资源字段”,无需多余元数据(如不用 {"data":{"name":"Alice"}},直接 {"name":"Alice"});

  • 批量操作可使用数组(如 POST /users/batch,请求体为 [{"name":"Alice"},{"name":"Bob"}])。

  • 响应体规范

  • 成功响应:包含“数据”和“元信息”(如分页、链接),示例:

// GET /users?page=1&size=10(分页查询用户)
{
  "code": 200,          // 可选(辅助客户端快速判断,核心仍依赖 HTTP 状态码)
  "message": "success",
  "data": {
    "items": [{"id":456,"name":"Alice"}, ...], // 数据列表
    "pagination": {
      "page": 1,        // 当前页
      "size": 10,       // 每页条数
      "total": 100      // 总条数
    }
  },
  "links": [{"rel":"next","href":"/users?page=2&size=10"}] // HATEOAS 链接
}
  • 错误响应:包含“错误码”和“错误描述”,示例:

// POST /users(缺少 name 字段)
{
  "code": 400,
  "message": "请求参数错误",
  "details": ["name 字段为必填项"] // 详细错误信息
}

四、RESTful 与其他 API 风格的对比

除 RESTful 外,常见的 API 风格还有 SOAP、GraphQL,三者的核心差异如下:

维度

RESTful

SOAP

GraphQL

核心思想

资源导向,通过 HTTP 方法操作资源

服务导向,基于 XML 协议调用远程方法

数据查询导向,客户端自定义所需数据

数据格式

以 JSON 为主,轻量

仅 XML,冗余(需包含信封、头信息)

自定义查询语法,响应为 JSON

接口设计

多个 URI(对应不同资源)

单个端点(如 /soap),通过 XML 区分方法

单个端点(如 /graphql),通过查询语句区分需求

灵活性

固定响应结构(客户端需多次请求获取关联数据)

灵活性低(方法和参数固定)

高(客户端按需获取数据,减少请求次数)

适用场景

通用 Web API(如移动端后端、第三方服务)

企业级复杂服务(如银行、支付系统,需强安全性)

前端灵活数据需求(如复杂页面、多端适配)

五、常见误区与最佳实践

1. 常见误区

  • URI 包含操作动词:如 /getUser/456(应改为 GET /users/456)、/deleteOrder/123(应改为 DELETE /orders/123)。

  • 滥用 POST 方法:所有操作(查询、更新、删除)都用 POST(违背 HTTP 语义,降低可读性)。

  • 忽略无状态:服务器存储会话(如 Session),导致无法水平扩展。

  • 不支持缓存:所有响应都设置 Cache-Control: no-cache,增加服务器负载。

2. 最佳实践

  • 使用 HTTPS:确保传输安全,避免数据泄露(如 Token、用户信息)。

  • 版本控制:API 迭代时通过 URI(如 /v1/users)或 HTTP 头(Accept: application/vnd.example.v1+json)进行版本控制,避免兼容问题。

  • 身份认证与授权:使用 Token(如 JWT、OAuth 2.0)进行认证,避免 Session;通过权限系统控制资源访问(如普通用户仅能操作自己的订单)。

  • 限流与监控:添加接口限流(如每秒最大请求数)防止过载,同时监控接口调用量、响应时间、错误率,及时发现问题。

总结

RESTful 不是一套强制标准,而是一种“约定优于配置”的架构风格。它通过资源导向无状态统一接口等约束,让 API 具备高可扩展性、可读性和可维护性,成为当前 Web API 设计的首选方案。

在实际开发中,无需严格遵守所有约束(如 HATEOAS 可根据复杂度选择性实现),但需把握核心原则:URI 描述资源,HTTP 方法表达操作,状态码告知结果,最终设计出简洁、易用、稳定的 API。

动物装饰