本节摘要:资源建模决定契约到底在管什么。本节教你识别业务领域里哪些概念够格当资源(通常指"能命名的名词"),把它们组织成对象、集合、子资源三类,并处理"动作"与"预支付账单"这类资源化困难户。资源建模直接决定后续 URI 长什么样、该用哪个方法,是四根构件的起点。
阅读完本节,你应当能够:
做过接口的人大概都见过这种返工:先把 URL 一个个写出来,写到一半发现"我要的是语义,不是字节",于是开头写的 /getUsers、/deleteUser 全推倒重来。前半段天真,后半段方法论。
资源建模存在的意义,是把"造什么接口"这个业务问题先于"怎么拼 URI"这个技术问题解决掉。业务没想清楚,URI 写得再漂亮也是沙上建塔;反之建模建清楚了,URI 几乎是被推出来的,不用费力想。
REST 的核心信条是"把世界拆成能命名的名词"。识别资源可以走一套快速筛查:
GET /users、GET /orders。GET /users/123/orders。一个概念如果无法稳定命名、或者只是"一顿操作"而非"一个东西",往往不适合直接当资源。拿三个典型例子对照:
| 概念 | 是否资源 | 处理方式 |
|---|---|---|
| 一个用户 | 是,对象 | 资源 users 下的实例 |
| 用户列表 | 是,集合 | 资源 users 本身 |
| 发送确认邮件 | 不是,动作 | 建"通知任务"资源或走过程状态 |
先把领域资源切割归纳成一张图,再顺着单元逐个展开:

拿电商订单域做一次完整切开。业务里有:用户能下单,订单包含若干订单项,订单有收货地址、有支付、有发货状态。
先识别资源,再画关系:
关系上,用户在 /users/123,他下单的订单列表在 /users/123/orders,订单 456 的明细在 /orders/456/items。注意一种取舍:订单是挂在用户下面还是独立成顶层?独立成顶层更常见——因为订单的业务生命周期与用户相对独立,且 GET /orders/456 不依赖你在哪个用户会话里。
{ "id": 456, "status": "paid", "userRef": "/users/123", "items": [ {"productRef": "/products/7", "qty": 2} ] }
上面的订单表示里,userRef 与 productRef 用完整 URI 指代关联资源,而不是塞一个裸的数值 id。这样客户端拿到表示,就能知道去哪 follow 关联——这是"通过表示操作"与后续 HATEOAS 的伏笔。
判断"挂子资源还是独立底层"还有一条经验:子资源只在"没有它父对象就没意义"时成立。订单项离开订单确实没意义,所以天然是 /orders/456/items;而商品、用户离开订单仍独立存在,所以留在顶层。先把实体间"依赖还是并立"的关系辨清,建模就不会为了"看起来整齐"而强行嵌套出一堆没必要的层级。
业务里总有被动词附身的时刻:sendEmail、activateUser、checkoutCart。硬要把它们塞成资源会拿到怪异的 URI。两种常规解法:
POST /orders/456/confirmations 创建一条确认记录,或 POST /orders/456/actions/confirm 明确表达"在订单 456 上执行确认动作"。前者偏资源,后者偏动作资源,工程上都常见。⚠️ 常见坑:逢操作就造
POST /sendEmail、POST /approveLeave一排动词端点。短期顺手,长期侵蚀资源体系的统一性。先尝试用"创建一条状态记录"来表达动作。
建模决策会一路传导到后面:你决定订单独立成顶层资源,URI 就少了 users/123/orders 这层深度;你决定支付是订单子资源,GET /orders/456/payments 就有了归属;你决定确认走动作资源,方法语义就要在 POST 与 201 上多做文章。所以本节是四根构件的"最先输入",返工成本也最高——建模错了,后面三节全跟着错。
💡 关键直觉:一个好模型应让客户端"不看文档也能猜到 URI"。资源的名字越贴合业务直觉,接口的自解释性越强,越经得起长期演进而不需要频繁改。
主体定盘,下一节就给这份主体登记一张规范的地籍图——URI 设计的每一笔都有讲究。