RESTful API
RESTful API 是一种架构风格和设计原则,而不是一个标准协议。它由 Roy Fielding 博士在其 2000 年的博士论文中提出,旨在为分布式超媒体系统(如 Web)提供一种结构化的设计方法。
核心思想:将网络上的数据和功能视为资源(Resources),并通过统一的接口(HTTP 方法)对这些资源进行操作。
目录
- 核心原则与约束
- RESTful API 的关键特征
- 为什么软件工程师喜欢使用/设计 RESTful API?
- 设计 RESTful API 的最佳实践
- RESTful 与 RPC 风格 API 的区别
- 总结
核心原则与约束
RESTful API 的设计遵循以下核心原则(也称为 Fielding 约束):
1. 客户端-服务器分离
- 客户端负责用户界面和用户状态
- 服务器负责数据存储、业务逻辑和安全
- 两者独立演进,通过 API 交互,提高了可移植性和可扩展性
2. 无状态
- 每次客户端请求必须包含服务器处理该请求所需的所有信息
- 服务器不保存客户端会话状态(如登录状态)。状态应该由客户端管理(例如通过 Token 放在请求头中)
- 优点:提高可见性(请求独立)、可靠性(失败后易恢复)、可扩展性(服务器无需维护状态,易水平扩展)
3. 可缓存
- 服务器响应必须明确或隐含地标记为可缓存或不可缓存
- 客户端(或中间代理)可以缓存响应,提高性能,减少服务器负载
- 通常通过 HTTP 头如
Cache-Control、Expires、ETag、Last-Modified实现
4. 统一接口
这是 REST 最核心的约束,定义了客户端与服务器交互的标准化方式,包含几个方面:
- 资源标识:每个资源(如用户、订单)在系统中都有一个唯一标识符 (URI)。例如:
https://api.example.com/users/123、https://api.example.com/orders/4567 - 通过表述操作资源:客户端通过操作资源的表述(Representation)来操作资源本身。表述通常是 JSON 或 XML 格式的数据。客户端发送表述来创建或修改资源(如 POST/PUT 请求体),服务器返回表述来表示资源状态(如 GET 响应体)
- 自描述消息:每个消息(请求/响应)包含足够的信息让接收方理解如何处理它。这通常通过 HTTP 方法、HTTP 状态码、Content-Type 头等实现
- HATEOAS:超媒体作为应用状态的引擎。这是 REST 成熟度模型中最高级别的要求。响应中应包含指向相关资源或可用操作的链接(Hyperlinks)。客户端通过跟随这些链接来驱动应用状态,而不是依赖硬编码的 URL 结构。例如,获取用户列表的响应中可能包含指向每个用户详情的链接,以及创建新用户的链接。虽然理想,但在实际项目中常因复杂性和客户端需求而被简化或部分实现
5. 分层系统
- 架构可以由多层组成(如负载均衡器、代理、安全层、应用服务器、数据库)
- 客户端通常不知道它是否直接连接到最终服务器还是中间层。这提高了可扩展性、安全性和可管理性
6. 按需代码(可选)
- 服务器可以临时扩展客户端功能,例如通过返回可执行的代码片段(如 JavaScript)。这在实际 API 设计中较少作为核心约束使用
RESTful API 的关键特征
1. 资源导向
- 一切皆资源(用户、订单、产品、配置项)
- API 端点 (URL) 应该清晰地标识资源(通常使用名词复数形式)
好的例子:
GET /users– 获取用户列表GET /users/{id}– 获取特定用户POST /users– 创建新用户PUT /users/{id}– 更新特定用户(全量替换)DELETE /users/{id}– 删除特定用户
坏的例子(非 RESTful):
GET /getAllUsersPOST /updateUser(应该用PUT /users/{id})/doSomething?action=deleteUser&id=123(动作放在 URL 参数或路径中)
2. 使用 HTTP 方法(Verbs)定义操作
RESTful API 充分利用 HTTP 协议的语义化方法来操作资源:
- GET:检索资源。安全(不应改变资源状态)且幂等(多次执行结果相同)
- POST:创建新资源。非安全(改变状态)非幂等(多次提交可能创建多个资源)
- PUT:更新整个资源(全量替换)。需要客户端提供资源的完整新状态。非安全,幂等(多次更新同一资源效果与一次相同)
- PATCH:更新资源的部分内容。非安全,幂等性取决于实现(通常设计为幂等)
- DELETE:删除资源。非安全,幂等(删除一次或多次,资源最终都不存在)
3. 使用 HTTP 状态码表示结果
服务器通过标准化的 HTTP 状态码告知客户端请求的处理结果:
200 OK:请求成功(GET/PUT/PATCH)201 Created:资源创建成功(POST),响应头Location通常包含新资源的 URI204 No Content:请求成功,但响应体无内容(常用于 DELETE 或 PUT/PATCH 更新后无需返回资源时)400 Bad Request:客户端请求错误(语法、参数无效)401 Unauthorized:未认证(需要登录但未提供有效凭证)403 Forbidden:认证成功但无权限操作该资源404 Not Found:请求的资源不存在405 Method Not Allowed:该资源不支持请求的 HTTP 方法409 Conflict:请求与当前资源状态冲突(如更新已被他人修改的资源)500 Internal Server Error:服务器内部错误503 Service Unavailable:服务暂时不可用(过载或维护)
4. 表述格式(通常 JSON 或 XML)
- 资源在传输时的表现形式。现代 API 绝大多数使用 JSON,因为它轻量、易读、易解析。XML 在某些遗留或特定领域(如 SOAP)中仍有使用
- 通过
Content-Type(请求头,表示客户端发送的数据格式)和Accept(请求头,表示客户端期望接收的数据格式)协商格式
5. 无状态通信
- 如前面所述,服务器不保存客户端会话信息。认证信息(如 API Key、JWT Token)必须包含在每个请求中(通常在
Authorization头)
为什么软件工程师喜欢使用/设计 RESTful API?
- 简单性与可理解性:基于 HTTP 标准,概念清晰(资源+方法),易于学习和使用
- 松耦合:客户端和服务器独立演化,只要接口(URI、方法、格式)不变
- 可扩展性:无状态设计使服务器容易水平扩展。缓存机制显著提升性能
- 可发现性:良好设计的 URI 和 HATEOAS(如果实现)使 API 更易于探索和理解
- 平台/语言无关:基于 HTTP 和标准数据格式(JSON/XML),任何能发起 HTTP 请求的客户端(浏览器、移动 App、其他服务)都可以使用
- 丰富的生态系统:大量工具支持(文档生成如 Swagger/OpenAPI,测试工具如 Postman/Insomnia,客户端库,网关,框架等)
设计 RESTful API 的最佳实践
- 使用清晰的、名词性的 URI:
/users、/orders/{orderId}/items。避免动词 - 正确使用 HTTP 方法:严格遵守 GET(读)、POST(创建)、PUT(全量更新)、PATCH(部分更新)、DELETE(删除)的语义
- 利用 HTTP 状态码:准确反映操作结果,不要所有成功都返回 200,所有错误都返回 500
- 版本管理:在 URI 中(
/v1/users)或请求头(Accept: application/vnd.example.v1+json)引入 API 版本,保证向后兼容或明确告知变更 - 提供一致的响应格式:即使是错误,也返回结构化的 JSON 信息(包含错误码、消息、详情等)
- 支持过滤、排序、分页:对于集合资源(
/users),使用查询参数如?page=2&size=20&sort=name,asc&filter=status:active - 考虑安全性:使用 HTTPS,实施认证(如 OAuth 2.0、JWT)和授权(RBAC、ABAC)
- 提供优秀的文档:使用 OpenAPI (Swagger) 规范自动生成交互式文档是行业最佳实践
- 适度使用 HATEOAS:即使不完全实现,在响应中包含相关资源的链接也能提升 API 的可发现性和易用性
RESTful 与 RPC 风格 API 的区别
- RESTful:关注资源和状态,通过标准 HTTP 方法操作资源。URI 标识资源
- RPC (Remote Procedure Call):关注动作或过程。API 端点通常直接对应一个函数或操作名(动词),如
/getUser或/placeOrder。参数通常通过请求体传递。虽然也能用 HTTP,但其设计哲学是基于调用远程方法
总结
作为一名软件工程师,理解并遵循 RESTful 架构风格是设计和构建现代、可扩展、易维护的 Web API 的关键。它利用 Web 的基础(HTTP)提供了一套强大的约束和最佳实践,促进了系统的互操作性、性能和可演化性。虽然 HATEOAS 等高级概念在实际项目中可能被简化,但核心原则(资源、URI、HTTP 方法、状态码、无状态)是构建高质量 API 的基石。掌握 RESTful API 设计是后端工程师和全栈工程师的必备技能。
