本节摘要:REST 由 Roy Fielding 定义的六大约束构成:客户端服务器、无状态、可缓存、分层系统、统一接口、按需代码。其中前五条是硬约束,按需代码可选。统一接口又拆成资源识别、表示操作、自描述消息、超媒体引导四项子约束,是整套风格最难也最能带来回报的部分。本节逐条拆解每条约束在管什么、省了它是什么后果。
阅读完本节,你应当能够:
把 REST 想象成一份施工总合同,六大约束就是六条需要在进场前逐条画钩的验收项。每条钩掉一个隐患:客户端服务器砍掉了"UI 与数据耦合"的隐患,无状态砍掉了"服务器记你上次干嘛"的隐患,可缓存砍掉了"重复拉拳脚"的隐患……它们不是独立的审美主张,而是六个保险公司——每缺一条,系统就要在特定维度上多扛一分风险。
一条条过。
这条约束最简单,也是整个分布式系统的地基:客户端管用户界面与交互,服务器管数据与业务逻辑,两者分开演进。
缺了这条约束会发生什么?服务端逻辑偷偷包含 UI 逻辑,前端任何一次改版都可能拉倒后端,契约的双方从此绑定成一体。
无状态要求:服务器在两个请求之间不保存客户端的会话上下文,每个请求都得自带处理它所需的全部信息。
"无状态"被误解最多。它不是说"不能有用户登录",而是说服务器处理单个请求时,不该依赖上一个请求留下的记忆。登录状态怎么装?由客户端拿令牌(如 JWT)装在每个请求头上带走,服务器验证令牌即知你是谁,不自己留那份会话。
缺了它,负载均衡后面挂多台机器时,同一用户的两次请求可能落进不同机器,机器间得同步会话,复杂度坐火箭上升。这一条的深入剧场在第 2 章的无状态契约节。
可缓存约束要求:服务器把响应标记为可缓存或不可缓存,允许客户端与中介把它们存起来复用,减少重复交互。
关键在于"明确或隐含地标记"。一个响应如果自带了 Cache-Control: max-age=3600,就是明说"这一小时内你可以直接复用,别再来烦我"。如果有条件缓存,还可以配合 ETag、Last-Modified 做验证式缓存,这个机制第 3 章缓存契约节会整套展开。
缺了它,热门接口的每次查询都穿透网络打到数据库,读多写少的场景下服务器会在没必要的地方被压垮。HTTP 原生给了缓存这套工具,REST 只是要求你善用它。
分层系统要求:客户端不知道也不在乎自己面对的是最终服务器还是中间层。
负载均衡器、反向代理、网关、共享缓存、安全过滤,这些中间件可以被静默插入,客户端看到的仍是同一个入口。这让系统的可伸缩性与安全策略有了独立的扩展空间——加一个缓存层给全站提速,客户端毫不知情。
缺了它,客户端如果硬编码了"我连接的就是最终服务器",那任何中间一跳都可能让响应里泄露"其实有个代理"的内部信息,改集群结构就得动客户端。
统一接口是六条里唯一"既简化系统又允许独立演进"的双赢条款,也是实践中最容易被打折的一条。它由四项子约束组成:
Content-Type、Content-Location 等元数据,就足以操作资源。Content-Type 说明体是什么格式。客户端不需要查别的文档就能解读这条消息。_links,像路标一样引导客户端下一步能做什么。四项子约束的价值排序值得先说透:资源识别和表示操作是"及格线";自描述消息接近及格线但经常被忽略;HATEOAS 是满分级,绝大多数真实 API 做不到,第 2 章会专门讲它。
{ "id": 42, "status": "paid", "_links": { "self": "http://api.example.com/order/42", "cancel": "http://api.example.com/order/42/cancel", "invoice": "http://api.example.com/order/42/invoice" } }
上面这个订单表示里,_links 就是超媒体引导——告诉客户端"你能撤销这个订单、能查它的发票",客户端据此导航,而不用把 URI 硬编码进客户端代码。
⚠️ 常见坑:把"有
_links字段"等同于做了 HATEOAS,结果链接只是装饰、没驱动客户端的行为。超媒体是"客户端靠链接活",不是"响应里多几个网址"。
按需代码允许服务器给客户端下发一段可执行代码(比如浏览器里的脚本),临时扩展客户端能力。Web 浏览器里的脚本执行就是它的典型体现。
它是六条里唯一可选的一条——因为下发可执行代码会破坏"客户端逻辑稳定"的预期,换来的是灵活性。绝大多数 API 不必上它,直接省略即可,省略不会让你失去 RESTful 的资格。
💡 关键直觉:把这六条想象成验收表:前五条该勾的一律勾,第六条结合场景决定。真正决定一套 API 成色的,是统一接口那四项子约束做到了几成。
下一节我们把六条契约拿去市场上比价,看看什么场景值得签、什么场景签了反而亏。