--- name: api-contract description: 用于接口契约设计(api contract)、通信协议选型(REST/RPC/GraphQL)、参数校验与统一错误结构规范。 --- # API 契约与接口设计 设计服务间通信协议、明确请求与响应规范、固化输入输出校验并统一错误返回结构。本 skill 关注**系统对外与模块间的通信契约**;内部业务逻辑由领域模块负责。 本 skill 保持**技术栈无关**,不预设具体网络框架或校验库,只提供协议权衡维度、结构化决策清单与防御性工程标准。 ## 何时主动介入 在实现过程中遇到以下情况时,停下来先把契约定下来,而不是先写实现再回头补: - **新增一个对外或跨模块调用的接口,但还没有结构化契约定义**:先确认字段、类型与校验规则,再写路由处理逻辑。口头约定或"看实现代码猜字段"都不算契约。 - **错误响应的字段结构和已有接口不一致**:指出偏差,要求统一到同一个错误外壳,而不是让新接口另起一套。 - **要给已上线接口删字段、改字段类型或收紧校验**:这是破坏性变更,先确认是否有存量调用方,并给出弃用窗口,不要直接改。 - **响应体直接序列化 ORM 实体或数据库查询结果**:立即指出潜在的字段泄露风险,要求换成显式的响应结构投影。 日常增量开发中新增字段、调整可选参数等不破坏兼容性的小改动,不必每次都重新过一遍完整决策清单。 ## 决策清单 ### 1. 通信协议与交互范式选型 根据调用方特征、网络拓扑与交互频率权衡以下协议范式: - **资源导向(RESTful HTTP)**: - 适用:公开 Web API、第三方集成、移动端或浏览器前端访问。 - 权衡:天然复用 HTTP 缓存、状态码与反向代理生态;但在复杂嵌套关联查询时容易发生多轮网络往返或过度获取数据。 - **过程导向(RPC / 二进制协议)**: - 适用:微服务内部通信、内部管理后台或对性能与网络带宽有严苛要求的场景。 - 权衡:方法级调用体验直观、序列化与反序列化开销小;但跨语言调用依赖代码生成,且无法直接使用常规浏览器调试工具或 CDN 缓存。 - **声明式查询(GraphQL)**: - 适用:多端多设备差异化展示、高度图状关联的复杂聚合展示层。 - 权衡:调用端按需索取字段;但服务端需要解决深层嵌套导致的 N+1 查询风暴,且 HTTP 层次的缓存策略更复杂。 - **流式与全双工(SSE / WebSocket)**: - 适用:大语言模型流式输出、实时通知推送(SSE 优选);双向高频交互或多人协同编辑(WebSocket 优选)。 - 权衡:长连接对负载均衡与网关并发连接数有额外要求,需设计心跳保活与重连补偿机制。 ### 2. 契约定义、校验与单一真实源 坚持“契约先于实现”或“强类型单一真实源”,杜绝口头约定与实现漂移: - **单一真实源(Single Source of Truth)**: - 必须维护统一的契约定义(如接口描述文件或统一导出的静态类型声明)。 - 客户端 SDK、服务端路由校验与接口文档必须由同一套源头生成或在 CI 中保持静态一致性校验。 - **严格入参校验**: - 类型与范围:对每个输入字段显式约束类型、长度、数值区间及正则格式。 - 严格剔除非预期字段:防止调用方传入未声明的额外属性,杜绝意外参数穿透引发的安全隐患。 - **严格出参投影与过滤**: - 严禁将数据库实体(ORM 对象)原样序列化输出,防止将密码散列值、内部审计字段或敏感租户 ID 泄露给外部。 - 响应对象必须显式定义结构投影,只返回客户端当前场景需要的白名单字段。 - **向后兼容与演进原则**: - 接口变更保持字段只增不减:新增字段必须为可选属性,不得破坏存量调用方的解包逻辑。 - 废弃过渡:废弃老字段或接口必须提前通过响应头(如 `Sunset` / `Deprecated`)或文档公示,保留足够弃用窗口期。 ### 3. 统一错误模型与响应设计 为接口消费方提供结构稳定、语义清晰、便于排障的错误反馈: - **协议层状态与业务状态解耦**: - 合理使用传输协议层状态码(如 HTTP 4xx 代表客户端请求问题,5xx 代表服务端故障),不应全部返回 200 再由外壳包裹错误码。 - 复杂业务失败(如余额不足、库存不足)在合适的状态码下,通过具体的业务错误码细化表达。 - **统一错误外壳(Error Envelope)规范**: - 错误响应体应具备一致的顶层字段,例如: - `code`:全大写下划线或命名空间格式的机器可读标识符(如 `INSUFFICIENT_FUNDS`),供调用方做程序分支跳转。 - `message`:人类可读的摘要提示,语言适宜直接展示给终端用户或开发者。 - `details`:表单级或字段级验证错误列表(包含字段路径与具体校验失败原因),便于前端精准标注高亮。 - `requestId` / `traceId`:全链路追踪标识,方便调用方提供日志关联定位问题。 - **安全脱敏与故障隔离**: - 生产环境下严禁在错误响应中返回未捕获的系统调用堆栈(Stack Trace)、数据库内部错误信息或底层服务器版本号。 ## 产出 契约必须落在一份结构化文件里,而不是只停留在对话或代码注释中:接口描述文件(如 OpenAPI/Protobuf/GraphQL Schema)或语言原生的静态类型声明均可,选哪种由已选定的通信协议决定,但必须能被 CI 校验、能生成客户端类型或文档。 协议范式(REST/RPC/GraphQL 三选一)与单一真实源的落地方式一旦选定,更换代价高,属于难以逆转的决策:用 `domain-modeling` skill 记一条 ADR,写清楚为什么选了这个而不是另外两个。