跳到主要内容

第 7 章 API 设计:业务动作如何变成接口

副题:资源、方法、状态码、版本——接口是业务的"对外承诺"

第 4 章我们翻译出了需求清单,第 6 章我们把业务模型翻译成了六张表。现在,业务线走到第四站,也是"从业务到系统"流水线的最后一张图纸:业务动作怎么变成接口?用户"发布内容"、卖家"上架商品"、买家"下单付款"——这些动作要变成前后端之间、系统之间的请求。第 6 章说"数据模型是接口的舞台,接口是数据模型的演员",本章就来看这些演员怎么上台。

本章要建立的认知

  1. 接口是业务的承诺。 URL 定资源,方法定动作,状态码定结果,错误码定边界——每一层都是契约。契约模糊,联调是灾难;未来拆服务时,更是定时炸弹。
  2. REST 的核心是三层契约,不是 URL 美学。 资源用名词(/orders),动作交给 HTTP 方法(GET/POST/PUT/DELETE),结果用状态码(200/201/400/404)。各管各的,别在 URL 里写动词,也别拿 200 包一切错误。
  3. 接口最大的代价在"改"。 一旦对外发布,它就是承诺。破坏性变更要发版本、要兼容、要维护两套逻辑。设计时多想五分钟,胜过上线后迁移一整天。

7.1 没有约定的系统​

六张表建完的第二天,你开始写接口。

需求清单里写明的业务动作是:注册登录、发布内容、评论、上架商品、下单付款。你打开编辑器,按"动作"给每个功能起了一个 URL——这是大多数人写接口的第一反应:

POST /createUser 注册
POST /login 登录
POST /createContent 发布内容
POST /addComment 评论
POST /createOrder 下单
POST /doPay 付款
GET /getUserInfo 看用户信息
GET /getContentList 看内容列表

两周后,前端同事(就是你雇的第一个兼职)开始联调。混乱从第一行代码开始:

第一个问题:没有统一的方法约定。 创建内容用 POST,获取列表你写的是 GET,但筛选项一多(分类、价格区间、时间、关键词),URL 超长,前端只能改用 POST 把参数放进请求体。回头看你的接口:create、get、add、do 混着用,POST 和 GET 也是凭感觉。前端每接一个新接口,先猜你的命名习惯,再猜你的方法规则。

**第二个问题:返回格式。**有的包一层 {data: ...},有的直接返回数组;有的成功返回 {code: 0},有的返回 {success: true}。前端每接一个新接口,先猜你的结构。

第三个问题:错误处理没谱。 登录失败,HTTP 200 + {code: 1001, msg: "密码错误"};下单失败,HTTP 500 + 一段 HTML。前端问:"到底什么时候看状态码,什么时候看 body?"你答不上来——因为你也没想清楚。

联调吵了三天,你忍不住抱怨前端难配合。但冷静下来想:发布内容——业务上是一句话,技术上却是一串没定义的问题:前端发什么请求、后端怎么响应、错了怎么办。你和前端谁都没约定过这三件事,却默认对方"应该懂"。

接口的本质,就是这三件事的约定:请求怎么发、成功怎么表示、错了怎么办。没有约定,联调是灾难;约定不清,每个接这个接口的人都要猜。

回头再看那三场吵架,其实是一个问题的三个面:方法之争是"请求怎么发"没定,格式之争是"成功怎么表示"没定,错误之争是"错了怎么办"没定。

还要认清一个现实:接口的读者不止前端。阶段 1 只有前端在调;第 16 章加缓存、第 26 章拆服务,都要回到这些接口;三年后接手的人,第一个读的也是它们。接口是系统里被阅读次数最多的代码,读着舒服是次要的,约定清晰可执行才是目的。

为什么新手开发者特别容易随手写接口?教程只教"功能怎么实现"(注册就是 insert 一下),从不教"约定怎么设计";一个人开发时没有冲突——格式对不上当场改,"约定"根本没有存在感。直到第二个人加入,矛盾才爆发,而那时接口已经写了几十个,改哪个都有人依赖。

所以接口设计值得在动手之前就想清楚。验收标准只有一句:调用方能不能不读代码就猜到约定。可猜性来自三个原则:

  • 一致:同一种操作永远同一种表达。列表都分页、错误都同构,不要这个包 data 那个不包。
  • 简单:URL 和参数越少,猜错的可能越小。
  • 标准:用 HTTP 已有的语义(方法、状态码、缓存头),不要自创。自创一套等于让所有人重新学。

本章就是教这三层约定怎么做:资源与方法(请求怎么发)、状态码(成功怎么表示)、错误码与版本(错了怎么办、改了怎么兼容)。

动手之前,先把阶段 1 的完整接口清单列出来——它是本章的"施工图",后面每节讲的约定都会落到这张表上:

方法URL用途
POST/v1/users注册
POST/v1/login登录
GET/v1/users/{id}用户信息
GET/v1/contents?page=&size=内容列表(分页)
POST/v1/contents发布内容
GET/v1/contents/{id}内容详情
POST/v1/contents/{id}/comments评论
GET/v1/products?page=&size=商品列表
POST/v1/products上架商品
POST/v1/orders下单(幂等键 order_no)
POST/v1/orders/{id}/pay付款
GET/v1/orders/{id}订单详情

对照第 4 章需求清单:每个业务动作对应一个接口,每行验收标准都能通过接口验证——接口清单就是需求清单在协议层的翻译。动手前花一天把约定想清楚,胜过联调时花几天在吵架里补规则。第 4 章算过需求分析的账(一天分析省两周返工),接口设计的账一模一样——约定是联调的地基,地基不打,楼就歪。

时间点要卡准:需求定稿之后、后端开工之前,立起接口清单。需求没定就设计,白做;代码写完再补接口,就是以后的联调灾难。接口设计是需求分析和开发之间的一道工序,和建表(第 6 章)在同一个位置:先画图,再施工。

7.2 第一层约定:资源与方法​

REST 的第一层约定,是把动作翻译成"资源 + 方法"。

7.1 的 URL 列表有个通病:URL 是动词(create、get、add、do)。动词式 URL 意味着每个接口都是新发明——前端每接一个都要学一个新词,动作无穷,URL 也无穷。

REST 的思路是反过来的:URL 只写名词(资源),动作交给 HTTP 方法。用户、内容、订单都是资源,资源数量有限,方法只有四个。组合起来就能表达所有动作:

GET /v1/users/{id} 看用户
POST /v1/users 注册
GET /v1/contents 看内容列表
POST /v1/contents 发布内容
GET /v1/contents/{id} 看单条内容
POST /v1/contents/{id}/comments 评论内容
POST /v1/orders 下单
POST /v1/orders/{id}/pay 付款

URL 数量从"和动作一样多"变成"和资源一样多",动作差异由方法表达。前端记住"有哪些资源 + 四个方法什么意思",就能猜出大部分接口。约定的价值,就是让"猜"变得可靠。

资源式 URL 有三个细节直接影响可猜性:

复数名词。 资源名用复数(/contents、/orders),因为 URL 首先指向集合,单条再用 id 定位(/contents/{id})。单复数之争不重要,团队统一才重要。

嵌套表示归属。 /contents/{id}/comments 表示"这条内容下的评论"。但嵌套不要超过两层,超过说明资源边界该重划了(第 6 章的实体边界判断同样适用)。

路径参数和查询参数分工。 路径参数定位资源(/contents/{id}),查询参数筛选集合(/contents?author=3&tag=二手)。定位用路径,筛选用查询,URL 才清晰。

一个真实选择:用户的订单列表,嵌套还是查询参数?

  • GET /users/{id}/orders(从用户入口进)
  • GET /orders?buyer_id={id}(从订单入口进)

两种都常见,选哪个取决于"谁是入口":用户详情页看订单用嵌套,订单管理后台用查询。同一个业务可以两种并存,但契约里必须写清语义——同一个 URL 不能有两种含义。

查询参数的筛选语义会牵出相关技术:/products?tag=二手 按标签筛商品。标签筛选走 product_tag 关联表(第 6 章),条件多了要建复合索引(第 14 章),结果慢了要加缓存(第 16 章)。一个查询参数,后面跟着一整条性能链路。

四个方法的语义要逐个说清:

  • GET:读资源,幂等。读十次和读一次结果一样,可以安全重试、可以缓存、可以被浏览器直接打开。GET 永远不应该有副作用。
  • POST:创建资源,或执行不被幂等语义覆盖的动作,不幂等。发两次创建两条。POST /orders 发两次产生两个订单——这正是第 17 章幂等机制的接口侧起点:客户端带 order_no,服务端靠唯一约束挡重复(第 6 章建表时埋的伏笔,在这里被接口暴露出来)。
  • PUT:整体更新,幂等。把资源全部字段替换为请求里的值,做两次结果一样。
  • PATCH:部分更新,只改请求里出现的字段。和 PUT 的边界(整体 vs 部分)容易混,团队里约定"更新二选一"即可,混用会乱。
  • DELETE:删除资源,幂等。删不存在的资源返回 404,也算结果一致。

注意幂等出现了三次。它首先是一条从接口就开始的语义约定:GET/PUT/DELETE 幂等,POST 不幂等。调用方(前端、重试机制、消息队列)根据方法就知道能不能安全重试。第 17 章交易系统的幂等判断(order_no 唯一键、状态前置检查),本质上是"把不幂等的 POST,在业务层做成幂等操作"。

最后说 POST 的幂等键。POST /v1/orders 本身不幂等,但业务要求"同一笔下单只执行一次"——约定是请求体里带 order_no,服务端拿它当唯一键。接口文档里要写明"哪些 POST 支持幂等键、幂等键放哪"——这行字,就是第 17 章幂等机制在接口侧的锚点。

图7-1 新 图稿占位
REST 接口设计示意
业务动作→资源+方法+状态码

还有一个容易踩的坑:资源不等于表。

第 6 章我们建了六张表,那是存储层的结构。现在你在设计的 REST 接口,是业务层的视图。两者经常长得不一样,因为它们本来就属于不同的语境。

举个例子你就明白了。POST /v1/contents 发布内容,确实对应着往 content 表里插入一行——这个映射很直白。但再看 POST /v1/orders/{id}/pay 付款,它背后触发的是订单状态流转(第 17 章会讲状态机),可能涉及订单表、支付流水表、库存表的好几行更新。你不会也不应该让前端直接操作这三张表,而是给它一个"付款"的资源入口。所以资源是"业务上的一件东西",表是"存储里的一个结构",接口设计看业务,表设计看存储,各司其职。

如果你把第 6 章和第 7 章连起来看,会发现业务对象其实经历了两次翻译:

表(存储结构)→ 资源(业务视图)→ 接口(网络协议)

六张表,翻译成了十来个资源,再变成十几个接口。每一次翻译都换了一层语言,但"用户""内容""订单"这些业务语义始终没变。这就是从业务到系统的完整流水线:

业务语言 → 需求清单(第 4 章)→ 技术方案(第 5 章)→ 表结构(第 6 章)→ 接口(第 7 章)

每一层都是上一层的转译,每一层都保留业务的灵魂,只是换了表达的载体。

说到这里,正好回答第 1 章埋下的一个问题:为什么接口返回前要过一层 DTO(Data Transfer Object数据传输对象)?直接把数据库查出来的结构返回行不行?

直觉上似乎可以:我查出一行数据,顺手返回给前端,多省事。但真这么做,两个问题迟早找上门。

第一个问题,表结构是存储细节,不是接口承诺。 content 表里有 body、status、author_id,但列表页其实只需要 title、author_name 和 created_at。你把整行数据返回,前端拿到一堆用不上的字段,更麻烦的是,有些内部字段(比如用户表的密码哈希、订单表的内部状态码)可能就这么暴露出去了。

第二个问题,表结构一定会变。 第 6 章说过,表不是建完就一劳永逸的,业务长了要加字段,量大了要拆表。如果接口直接映射表结构,表一改,接口就破——所有调用方都要跟着改。DTO 就是接口和表之间的缓冲层:表怎么变,接口返回的结构保持稳定。

当然,DTO 也有代价,就是多写一层转换代码。但换来的是接口的长期稳定,这笔账很划算。第 12 章讲服务端分层时,我们会看到 DTO 在代码里正式登场。

接下来是两个从第一天就要约定的细节。

第一个是分页。 GET /v1/contents 如果不做分页,默认返回全部内容。初期数据少,似乎没问题,但内容只会越来越多,这个接口迟早变成第 16 章性能危机的第一个主角。所以底线是:列表接口从第一天就带分页。

/contents?page=1&size=20,返回 {items: [...], total: 1234, page: 1, size: 20}。至于从 0 还是从 1 开始计数、要不要返回 total、字段名叫 items 还是 list,各团队有自己的口味,选一个团队统一的写进契约就好。

第二个是响应体结构。 这就是 7.1 里前端和你吵的第二件事。我的建议是趁早统一:要么所有接口都包一层 {data: ..., meta: ...},要么都直接返回数据本体。两种风格没有绝对优劣,但混用就是灾难——前端每接一个新接口都要先猜一遍包装格式。

案例里的约定是这样的:列表接口返回 {items, total, page, size},单资源接口直接返回资源对象。 两种形态都在接口契约里写得清清楚楚。至此,"成功怎么表示"这个问题,就有了明确的答案。

7.3 第二层约定:状态码​

请求怎么发定了,下一个问题:**成功怎么表示?**REST 的第二层约定是 HTTP 状态码——它不是"后端顺手返回的数字",是机器可读的"结果语义"。

状态码按百位分成五类,每类一个意思:

状态码含义什么时候用案例
2xx成功了200 读成功、201 创建成功、204 删除成功POST /orders → 201
4xx调用方错了400 参数不对、401 未登录、403 没权限、404 资源不存在、409 冲突库存不足 → 409
5xx服务端错了500 内部错误、503 服务不可用数据库挂了 → 500

五类状态码是"结果"的五种可能:成功、你错了、我错了。调用方(前端)拿到状态码,第一件事就是判断该走哪条路:2xx 走成功分支,4xx 提示用户检查输入,5xx 提示"稍后再试"(并触发重试逻辑——第 17 章的回调重试就是靠状态码区分"重试有用吗")。

状态码的完整使用,看一次真实的接口调用:用户下单,POST /v1/orders——参数不对返回 400(前端提示检查输入)、没登录返回 401(前端跳登录页)、库存不足返回 409 + OUT_OF_STOCK(前端提示缺货)、一切正常返回 201 + 订单对象(前端跳转订单页)。一次调用,四种结果,每种都有明确的下游动作——这就是状态码语义的全部价值。

状态码的两个使用纪律:

**第一,4xx 和 5xx 不能混。**参数错误返回 500,前端会以为"服务器挂了"而重试;服务器出错返回 400,前端会提示"你输入错了"而让用户反复提交。状态码是"责任声明":4xx 是调用方的责任,5xx 是服务端的责任——责任分清楚,重试和提示才有依据。

第二,状态码管"结果",业务码管"细节"。POST /v1/orders 返回 409(冲突),调用方知道"下单没成功、不是我的网络问题",但不知道具体为什么——是库存不足?还是重复下单?这时候需要响应体里的业务错误码:

HTTP/1.1 409 Conflict
{
"code": "OUT_OF_STOCK",
"message": "商品库存不足",
"request_id": "a1b2c3"
}

code 是给程序看的(稳定、机器可读、前后端都有对应表),message 是给人看的(可以展示给用户),request_id 是给排障用的(第 25 章可观测性会展开:出问题时报这个 id,日志一查一个准)。状态码回答"成没成功",业务码回答"为什么",消息回答"怎么办"——三层各司其职,前端才不会需要读后端代码猜错误。

状态码的粒度,要让调用方"一看就知道下一步该做什么"。

2xx 内部要区分:200 读成功,201 创建成功,204 删除成功。创建返回 200 而非 201,前端无法判断"到底有没有创建新东西"。

4xx 要分清场景:401 是未登录(该跳登录页),403 是登录了但没权限(该提示无权访问),混用会让前端排查半天才发现只是登录态失效。404 也有两种用法:资源真不存在,或"存在但你没权限看"(用 404 隐藏存在,防探测),选哪种写进契约。

判定顺序建议固定:先鉴权(401)→ 再参数(400)→ 再业务(409)。顺序乱了,前端会先收到业务错误,排查半天才发现是没登录。

错误响应结构必须全项目统一,建议 {code, message, request_id}。code 是机器判断的依据(稳定、唯一、全项目一份文档),message 给人看,request_id 用来定位日志。前端写一套错误处理就能接所有接口,不必每个接口猜一遍。

重试策略先记一条简单规则,第 17 章会展开:5xx 可以重试(服务端问题,可能刚好恢复),4xx 不要重试(调用方问题,重试一万次也一样)。写进接口文档,前端和重试机制就都有了行为依据。

7.4 接口是承诺:契约与版本​

前两节解决了"请求怎么发、成功怎么表示",还剩最后一个、也最重的问题:接口定了之后,还能改吗?

答案是:看有没有人已经在用。没人用的接口随便改;一旦前端接上了、或者其他服务在调,接口就是承诺。你承诺了 POST /v1/orders 长这样,对方的代码就按这个写了。改接口等于改所有人的代码。前端这么要求不是娇气——接口是系统之间的契约,两边都依赖它。

所以接口文档要当契约用,不能只当"说明书"。说明书描述现状,契约声明"什么算数"。契约要机器可读(OpenAPI),因为机器要拿它干活:前端生成客户端代码和类型定义,测试生成用例骨架,后端做响应格式自检。一份契约三方复用,这才是"文档即契约"的价值。文档漂移(代码改了文档没改)是契约失效的头号原因,机器可读 + 自动化校验是唯一的解药。

维护契约有一条铁律:变更时先改契约,再改代码。流程是:改契约 → 评审 → 后端按新契约实现 → 前端按新契约生成客户端 → CI 里契约校验。契约是唯一真相,代码跟着契约走。顺序一旦反过来,三个月后端和文档就对不上了,回到"读代码猜约定"的原始时代。

契约还有一个隐藏价值:契约测试。前后端按同一份契约各自开发,联调从"两边对着凑"变成"验证是否违反契约"——这是第 15 章联调从地狱变例行的关键。

一份接口文档条目长什么样?以 GET /v1/contents 为例:

  • 方法/URL:GET /v1/contents?page=&size=,内容列表
  • 参数:page(页码,从 1 起)、size(每页条数,默认 20,上限 100)、author(可选)、tag(可选)
  • 响应:200 返回 {items, total, page, size};400 参数不合法
  • 错误码:无

四部分对应本章四层约定:怎么调、传什么、返回什么、错了怎么办。写全了,前端不用问;写不全,每接一个接口都要来问一次。

条目还可以加两栏,给未来铺路:

  • 权限标注(第 13 章):哪些接口要登录、哪些只能本人/管理员操作。第 13 章实现认证中间件时,照着这张表做就行。
  • 性能预算(第 16 章):每个接口标注 P95 目标,比如 GET /v1/contents < 200ms。没有预算,优化时只能凭感觉"好像有点慢"。

OpenAPI 就是把这些写成机器可读的 YAML:

/contents:
get:
summary: 内容列表
parameters:
- { name: page, in: query, schema: { type: integer } }
- { name: size, in: query, schema: { type: integer, default: 20 } }
responses:
'200':
description: 成功
content:
application/json:
schema: { type: object, properties: { items: { type: array } } }
'400': { description: 参数不合法 }

这份 YAML 是契约的实体形态:前端生成客户端、测试生成用例、后端做自检,都从它来。

接口要改怎么办?版本化。

业界约定俗成的做法:URL 里带版本号,/v1/orders、/v2/orders。规则只有一条:v1 永远不破坏,新东西进 v2。v1 的字段只能加不能删,删了会破坏旧前端;v2 是全新承诺,等所有调用方迁过去,v1 才能下线。

版本化的代价是并存:两套逻辑、两套测试。所以别轻易开新版本——能用"加字段 + 兼容"解决的,不要开 v2。什么时候必须开?语义变了,比如"下单"从一单一件变成一单多件,返回结构重写,这种破坏性变更才进 v2。

版本号放哪?放 URL(/v1/orders)是主流——简单、直观、谁都能看见。放 Header 更"干净",但调试麻烦、看不见。1-3 年团队选 URL 就对了,可见性比优雅重要。

用案例推演一次:阶段 3 加购物车,下单从一单一件变成一单多件。POST /v1/orders 的请求体从 {product_id, quantity} 变成 {items: [...]}——旧前端解析不了,属于破坏性变更,必须开 /v2/orders。v1 继续服务旧调用方,迁移完成后下线。你看,砍一个需求(第 4 章砍掉购物车),后端开发和这次接口迁移都省了。接口层也会少一次 v2。

向后兼容有张简单的规则表,值得记住:

  • 加字段:安全,前端不认识会忽略
  • 改字段类型:危险,前端按 string 解析你改成 number,全线崩
  • 删字段:危险,前端还在用
  • 改语义:最危险,字段没变但含义变了(比如"金额"从含税变不含税),前端代码一行不改,业务全错

所以 v1 的演化原则是:只加不改不删。要改要删,进 v2。这条规则,第 26 章服务拆分时还要再用一遍。

接口下线(v1 退休)也有流程:先通知所有调用方 → 观察流量 → 灰度拒绝 → 正式下线。每一步都在回答同一个问题:还有没有人依赖这个承诺?

回到案例:阶段 1 全是 v1,只有内部前端在用。但版本化的规矩必须在第一个接口上线前立好——因为第 16 章优化、第 26 章拆服务时,接口一定会变。那时"承诺"的约束才会真正显现。接口设计里最便宜的一步,是上线前想清楚版本策略;最贵的一步,是上线后发现从没想过。

7.5 REST 的边界:什么时候它不够​

REST 是 Web 接口的事实标准,但它不是唯一答案,更不是所有场景的最优解。大多数团队"默认用 REST"时,往往没意识到自己其实做了一次选型。

用第 5 章的决策模型过一遍这个选择。业务目标:让前后端(以及未来拆分后的服务之间)高效约定动作。当前约束:阶段 1 只有内部前端调用,接口几十个,团队一两个人。候选方案主要是 REST 和 RPC(比如 gRPC,接口像函数调用 getUser(id),而非 GET /users/{id})。评价维度聚焦可读性和生态——内部前端加浏览器环境,REST 的直观性和 HTTP 工具链最省心。收益是约定简单、调试方便;代价是语义表达力有限,复杂查询和多资源聚合得绕点路。演进条件也清楚:哪天出现强类型的跨语言服务间高频调用(第 26 章拆服务之后),或者条件多到查询参数堆不下,再重新评估 RPC 或 GraphQL。然后把这个选择压缩成决策记录,写进接口文档附录。

REST 还有两个"隐藏福利",和后面章节直接相关。

一是缓存语义。GET 表示幂等的读,天然可缓存。第 11 章 CDN 缓存静态资源、第 16 章给接口加缓存,靠的都是"GET 语义 + 缓存头"。如果当初把查询接口设计成 POST,缓存这层就没法做。

二是生态。HTTP 状态码、URL、JSON 是所有语言和工具的公共语言。浏览器调试、压测、网关、日志系统,全都认得。这其实是"资源 + 方法 + 状态码"三层标准约定带来的副产品——约定越标准,生态越能帮你。

REST 也有两个边界,阶段 1 碰不到,但心里要有数。复杂查询时,REST 只能堆查询参数来表达"按作者 + 标签 + 时间筛选",GraphQL 用声明式查询解决,但引入查询语言本身也有复杂度。服务间高频强类型调用时,RPC(如 gRPC)性能更好、类型更严,但那是第 26 章拆服务之后的话题。

再说两个细节。

登录接口。POST /v1/login 是个"动作"而非"资源"——它没有对应的表,也不符合"名词 + 方法"的形态。REST 社区对此的共识是:动作可以存在,但要少。登录、支付这类动词接口,一个系统通常只有几个,且要明确标记为例外。登录返回什么?会话凭证(token)。怎么生成、怎么校验、怎么过期,是第 13 章的主角。第 7 章只需把接口立起来:接受账号密码,返回凭证,401 表示无效。

破除一个常见误解:REST 不是"URL 好看",更不是"JSON 接口"。返回 JSON 但动词式 URL、状态码乱用、错误码缺失,依然是烂设计。反过来,URL 不花哨,但遵守"资源 + 方法 + 状态码"的语义,就是合格的 REST。判断标准只有一个:调用方能不能不读代码就猜到约定。

HTTP 是唯一所有人都认得的协议,REST 能成为 Web 世界的默认语言,靠的就是这一点,跟优雅无关。把接口约定建立在 HTTP 上,等于建立在所有人的共识上。这一章的每一层约定——资源、方法、状态码、版本——都是把这份共识变成可执行的细节。第 10 章会下到协议层,看 HTTP 本身怎么工作——那是接口约定之下的地基。

7.6 本章对应表​

业务诉求技术选择为什么代价/取舍
前后端各说各话REST 约定(资源+方法+状态码)统一的"动作语言",可读、可缓存、生态成熟语义要设计,动词式 URL 的习惯要改
接口不知道成没成功HTTP 状态码(2xx/4xx/5xx)机器可读的结果语义,责任声明(4xx 你错/5xx 我错)语义要统一,混用会误导调用方
业务错误说不清业务错误码(code+message+request_id)状态码管结果、业务码管原因、消息管怎么办错误码表要维护,前后端要对齐
接口不能随便改版本化(/v1/、/v2/)接口是承诺,改要兼容;v1 不破坏、新东西进 v2多版本并存的双倍维护成本
文档没人信文档即契约(OpenAPI)机器可读契约:生成客户端/测试/自检文档要维护,漂移要防
动作复杂、REST 不够(预告)RPC/GraphQL(边界)REST 是默认不是唯一;九步决策在 7.5场景没到不上(第 26 章服务拆分再评估)

到这里,第 4-7 章的流水线走完了:需求清单(要什么)→ 技术选型(怎么做)→ 数据模型(怎么存)→ 接口(怎么调)——四张图纸,把"一个社区交易产品"从一句话变成了可以开工的完整设计。从第 8 章开始,书要从"画图纸"切换到"看运行时":图纸上的一切(表、接口、选型),将在一台真实运行的机器上被一次真实的请求串起来。

本章小结​

  • 接口是业务的承诺:请求怎么发(资源+方法)、成功怎么表示(状态码)、错了怎么办(错误码)、改了怎么兼容(版本)——四层约定,每一层都在减少"读代码猜约定";
  • REST 的核心是资源是名词、动作是方法、结果是状态码,不是"URL 好看"——约定让"猜"变得可靠;GET/PUT/DELETE 幂等、POST 不幂等,是第 17 章幂等机制的接口侧起点;
  • 接口的代价在"改":契约要机器可读(OpenAPI)、改动要版本化(v1 不破坏、新东西进 v2)——最便宜的一步是上线前想好版本策略;
  • REST 是默认不是唯一:复杂查询(GraphQL)、服务间强类型调用(RPC)是已知边界,阶段 1 不引入——和砍需求同一个决策逻辑;
  • 本章画完"从业务到系统"的最后一张图纸:需求清单(4)→ 技术选型(5)→ 数据模型(6)→ 接口(7)——业务线的前半段走完,接下来视角切换:一次请求真的从浏览器出发,看它在运行时里怎么走(第 8 章起)。

下一章​

接口定好了:POST /v1/contents、GET /v1/contents……前后端对着契约写代码。但契约只是约定,请求真正发出去的那一刻,会发生什么?用户在浏览器输入网址、按下回车——URL 怎么变成 DNS 查询、HTTP 请求、服务器响应、页面渲染?

下一章开始,视角切换:从"业务怎么变成系统"切换到"一次请求在系统里怎么走"——第 8 章,请求的第一站:浏览器与前端基础。