Day34-10|15.4.4 REST 规范:以资源和统一接口构建轻量服务
Day34-10|15.4.4 REST 规范:以资源和统一接口构建轻量服务
一、教材定位与学习目标
15.4.4“REST 规范”介绍面向 Web 和互联网 API 的一种重要服务设计风格。REST(Representational State Transfer)以资源为核心,通过统一接口、资源表示、无状态通信、可缓存和分层系统等约束,使客户端能够以较简单的方式访问和操作网络资源。
高级系统架构设计师考试不会只考 HTTP 方法的含义,而会考 REST 如何与 SOA、微服务和云原生结合:资源应该如何建模?GET、POST、PUT、PATCH、DELETE 如何使用?接口如何保证幂等?如何处理分页、过滤、错误和版本?REST 与 SOAP 什么时候各自更合适?本篇以企业 API 和订单服务为例展开。
二、REST 的基本思想
REST 不是一个必须安装的产品,也不是“用 HTTP 返回 JSON”这么简单。它是一组约束,主要包括客户端—服务器分离、无状态、可缓存、统一接口、分层系统和按需代码等。实际企业 API 不一定完全满足所有约束,但应理解这些约束带来的架构收益。
REST 的核心抽象是资源。用户、商品、订单、文件和支付单都可以被建模为资源,每个资源有唯一标识,客户端通过资源表示与服务交互。
客户端 → 统一接口 → 资源服务
↓
资源表示、状态码和链接
资源与数据库表并不等同。订单资源包含业务状态和允许操作,不能简单把订单表的全部字段暴露为接口;一个业务动作也可能通过资源状态变化或专门动作接口表达。
三、REST 的主要约束
3.1 客户端—服务器分离
客户端负责用户体验和交互,服务器负责资源、业务规则和数据。双方通过契约协作,可以独立演进。移动端、Web 和第三方渠道可以使用同一业务资源,但表示和页面流程不必完全相同。
3.2 无状态
每个请求应包含完成处理所需的信息,服务器不依赖前一个请求保存在某个实例内存中的会话状态。状态可以保存在客户端令牌、数据库或共享缓存中,便于服务实例扩展、迁移和故障替换。
无状态不代表业务没有状态。订单的待支付和已支付状态仍然存在,只是它不依赖某台应用服务器的本地内存。认证令牌、幂等键和资源版本需要通过安全方式传递和管理。
3.3 可缓存
客户端、网关和 CDN 可以缓存明确允许缓存的响应,减少服务和数据库压力。缓存策略必须考虑资源版本、过期、权限和敏感数据,订单和个人信息不能因为配置错误被公共缓存。
3.4 统一接口
资源通过统一方法访问,响应使用一致的表示和状态码,降低客户端与服务之间的特殊耦合。统一接口包括资源标识、资源操作、消息自描述和超媒体等思想。
3.5 分层系统
客户端不需要知道请求经过 CDN、网关、缓存、代理和服务多少层。每层可以独立治理安全、缓存、路由和负载,但不能破坏端到端契约。
3.6 按需代码
服务可以向客户端返回可执行代码或动态能力,但企业 API 中使用较少。考试中了解其含义即可,重点仍在资源、统一接口、无状态、缓存和分层。
四、资源建模
4.1 资源命名
资源 URI 应稳定、可理解并体现层级关系。例如 /customers/{id}/orders 表示某客户的订单集合,/orders/{id} 表示单个订单。URI 通常描述资源,而不是把大量动词写在路径中。
4.2 资源与动作
查询订单使用 GET,创建订单可以使用 POST,替换资源使用 PUT,部分更新可使用 PATCH,删除或取消要根据业务语义设计。取消支付、确认收货等动作不一定是普通 DELETE,可以设计为状态变更或明确的动作资源,并定义权限、幂等和状态迁移。
4.3 聚合资源
客户端页面可能需要订单、商品、物流和会员信息。可以提供面向场景的聚合资源或 BFF,减少客户端多次调用,但聚合层不能重复实现核心规则,也不能让一个接口变成无限膨胀的万能查询。
4.4 资源表示
同一资源可以有 JSON、XML 或其他表示。表示应包含字段语义、版本、链接、时间、金额和状态。金额要明确精度和币种,时间要明确时区,敏感字段要脱敏。资源表示不应直接暴露内部数据库字段和实现细节。
五、HTTP 方法与幂等性
| 方法 | 常见语义 | 幂等性关注 |
|---|---|---|
| GET | 获取资源或集合 | 不应产生业务副作用 |
| POST | 创建资源或提交处理 | 通常非幂等,需业务幂等键 |
| PUT | 按指定 URI 创建或整体替换 | 设计为幂等 |
| PATCH | 部分更新 | 需明确重复执行语义 |
| DELETE | 删除或解除资源 | 通常设计为幂等,但业务需确认 |
HTTP 方法的标准语义不能代替业务幂等。POST 创建订单时,客户端重试可能产生重复订单,应使用请求幂等键;支付回调和退款也要以业务流水去重。即使使用 PUT,如果服务端每次调用都生成新的副作用,也不能称为真正幂等。
六、HTTP 状态码与错误模型
状态码帮助客户端区分请求结果,但业务错误不能全部塞进 200。常见类别包括:
| 类别 | 含义 | 例子 |
|---|---|---|
| 2xx | 请求被成功处理或受理 | 查询成功、创建成功、异步受理 |
| 3xx | 重定向或缓存相关 | 资源位置变化 |
| 4xx | 客户端请求或权限问题 | 参数错误、未认证、无权限、资源不存在 |
| 5xx | 服务端或依赖故障 | 内部错误、网关错误、依赖超时 |
错误响应应包含统一错误码、消息、关联请求号、可重试标识和必要细节。不要把堆栈和内部 SQL 返回给客户端。客户端根据错误类型决定修正参数、重新认证、有限重试、提示处理中或联系人工。
异步创建可以返回“已受理”以及任务或资源地址,客户端通过查询获得最终状态。接受请求不等于业务最终成功,尤其是支付、订单、文件处理和长流程。
七、分页、过滤与并发控制
集合查询需要支持分页、排序、过滤和字段选择,避免一次返回无限数据。分页方式可以是页码或游标,数据量大、实时变化频繁时游标通常更稳定。接口要限制最大页大小,防止恶意或错误请求消耗大量资源。
并发更新时,可以使用资源版本、ETag 或 If-Match 等机制检测客户端是否基于旧数据修改,避免后写覆盖先写。对订单、库存和账户等资源,版本控制要与业务状态机和权限结合,不能只靠 HTTP 头解决所有一致性问题。
八、缓存与条件请求
静态内容和公开商品信息适合 CDN 或网关缓存;个人订单、支付状态和敏感数据要限制缓存或按用户隔离。响应应根据 Cache-Control、过期时间和资源版本制定策略。
条件请求可以让客户端携带资源版本或修改时间,服务器在资源未变化时返回未修改结果,减少传输。缓存失效和并发更新仍需业务设计,价格和库存等动态数据在交易时要重新确认。
九、版本与兼容治理
REST API 版本可以通过 URI、请求头、媒体类型或网关路由表达。版本策略应尽量保持向后兼容:新增可选字段和新资源通常风险较低,删除字段、修改语义和改变必填规则需要迁移窗口。
服务端应统计消费者、版本和调用量,发布新版本时进行契约测试、灰度和回滚。数据库变更采用兼容迁移,消息和 API 不能因为内部字段变化而突然失效。版本不是越多越好,长期维护多个版本会增加安全和运维成本。
十、REST 安全设计
REST 接口应使用安全传输、身份认证、授权、输入校验、限流和审计。令牌应有有效期、受众和权限范围,不能把敏感信息直接放在 URL;权限要按用户、租户、组织、资源和动作控制。
防重放、幂等和 CSRF 防护根据认证方式和业务场景设计;文件上传限制类型、大小和存储路径;错误和日志脱敏;网关可提供 WAF、认证、限流和审计,但业务服务仍需做业务授权和数据校验。
十一、REST 与 SOAP 的选型
REST 适合 Web、移动端、公开 API 和轻量服务,使用 HTTP 生态、缓存和代理较方便;SOAP 适合强契约、跨组织、消息级安全、可靠消息和复杂企业扩展。REST 不是天然安全或可靠,SOAP 也不是天然高可用,二者都需要认证、授权、幂等、超时和治理。
选型应考虑:消费者类型、协议生态、消息复杂度、性能、跨组织互操作、可靠性、安全、事务、版本和团队能力。企业可以对外使用 REST,对内部遗留系统通过 SOAP 或适配器集成,不必全系统使用一种风格。
十二、案例分析:零售订单 REST API
某零售平台为商城、门店和第三方渠道提供订单 API。要求支持查询、创建、取消、支付状态和物流查询,第三方可能重试请求,订单数据不能越权访问。
可以将订单建模为 /orders/{id},集合查询支持分页和游标;创建使用 POST 并强制请求幂等键,服务端保存幂等结果;取消不简单删除订单,而是通过明确的状态迁移接口或动作资源,根据当前状态判断是否允许;支付状态通过查询和异步回调更新,不能只依赖客户端;响应使用统一状态码、业务错误码和请求标识;订单查询按用户、门店和渠道权限过滤;敏感字段脱敏,接口使用 HTTPS、令牌和审计;商品和物流查询可以缓存,订单和支付状态限制缓存;版本通过 URI 或媒体类型管理,使用契约测试和灰度发布。
服务内部仍使用本地事务、状态机和数据库约束,支付和物流跨系统使用事件、幂等、补偿和对账。REST 只规定接口风格,不代替业务一致性设计。
十三、课后练习
练习一:选择题
下列哪项最符合 REST 资源设计思想?
A. /doCreateOrderNow
B. /orders/{id} 并通过统一方法和状态表达订单操作
C. /database/table/order
D. /executeSql
答案:B。 REST 以资源为核心,避免把数据库结构和内部动词直接暴露为公共接口。
练习二:简答题
为什么 POST 创建订单通常需要额外的幂等键?
参考答案: POST 常用于创建资源,网络超时或响应丢失时客户端可能重试;如果服务器无法识别同一业务请求,可能重复创建订单。幂等键与用户、商品或业务单关联,服务端保存处理结果和状态,重复请求返回原结果或当前状态。支付、库存和消息处理还要分别使用业务流水和唯一约束。
练习三:案例题
某 REST API 所有请求都返回 HTTP 200,业务错误写在 success=false 字段中;客户端无法判断哪些错误可重试。请分析改进。
参考答案: 统一返回 200 混淆了传输成功、业务受理和处理失败,网关、监控和客户端无法按错误类别治理。应使用适当 HTTP 状态码区分参数、认证、授权、资源不存在、冲突、限流和服务端故障;响应体统一包含业务错误码、消息、请求标识、重试建议和必要细节;业务拒绝与技术失败分开,客户端根据错误类型决定修正、重新认证、有限重试或等待查询。写操作必须幂等,异步操作返回受理状态和资源查询地址;网关和监控按状态码、错误码和业务指标告警,日志脱敏并保留审计。
十四、论文写作素材
本节适合写“论 REST API 设计与治理”“论面向互联网的服务架构”或“论企业 API 的安全与可靠性”。论文可围绕资源建模、统一接口、无状态、缓存、分页、版本、幂等、错误和安全展开。
写作时要避免把 REST 简化成“HTTP+JSON”。应说明资源不是数据库表,状态码和错误模型要支持治理,缓存不能泄露敏感数据,POST 需要业务幂等,异步受理不等于最终成功,接口版本要兼容和灰度。最后以延迟、错误率、重复率、缓存命中率和接口可用性验证。
十五、今日复盘清单
- 我能否解释 REST 的主要架构约束?
- 我能否把用户、订单和商品建模为资源?
- 我能否正确使用 HTTP 方法、状态码和错误模型?
- 我能否说明无状态、缓存、分页、并发控制和版本治理?
- 我能否为 POST、支付和异步回调设计幂等?
- 我能否比较 REST 与 SOAP 的适用场景?
本节的核心结论是:REST 通过资源、统一接口、无状态、可缓存和分层等约束,提供适合 Web 和互联网服务的轻量协作方式。良好的 REST API 不只是返回 JSON,而是要同时设计资源边界、状态语义、幂等、错误、版本、缓存、安全和业务一致性。