接口规范
接口规范(API Contract)定义调用双方稳定依赖的请求、响应、错误、安全、版本与行为语义,是实现、文档、测试、Mock 和客户端生成的共同边界。
[!info] related notes
- REST 风格: RESTful API
- 机器可读描述: OpenAPI 与 Swagger
- 错误契约: 稳定 API 错误模型
- 认证与安全: API 认证与安全
- 版本: API 版本管理
- 幂等: 接口幂等设计
- 测试: Node.js API 集成测试
- 工具: Postman Mock、文档与 Spec Hub, APIFox
接口规范
一句话定义
接口规范是 API 调用方与实现方共同依赖的可验证契约。
它不只是“接口文档”,而是在回答:
我可以发什么?
服务器承诺返回什么?
失败时怎样表达?
什么行为稳定?
什么变化会破坏调用方?
一个完整 API Contract 至少有哪些维度
1. Endpoint / Resource Identity
明确:
- path;
- resource identity;
- path parameters;
- query semantics。
例如:
GET /users/{user_id}
这里不仅是字符串路径,还隐含:
user_id是什么类型;- 找不到资源怎样表达;
- 调用是否安全、可缓存。
2. Method / Operation Semantics
明确 HTTP method 或 RPC operation 的业务语义。
例如:
- GET 是读取;
- POST 可能创建或触发命令;
- PUT / PATCH 如何区分;
- DELETE 删除后重复请求如何处理。
这部分如果采用 REST 风格,继续看 RESTful API。
3. Input Contract
包括:
- headers;
- path/query parameters;
- request body;
- content type;
- required / optional;
- validation constraints;
- default values。
例如:
{
"display_name": "Alice"
}
还必须说明:
- 字符串长度;
- null 是否允许;
- 缺字段和显式 null 是否语义不同。
4. Output Contract
不仅要说“返回 JSON”,还要定义:
- HTTP status;
- body shape;
- field type;
- optionality;
- pagination / cursor;
- resource version / metadata;
- content type。
成功响应不要求所有项目都统一成:
{ "success": true, "data": ... }
是否使用 envelope 是项目级设计选择,不是 API 规范本身的定义。
5. Error Contract
错误应该同时考虑:
transport status
+
stable machine-readable code
+
human-readable message
+
optional structured details
详见 稳定 API 错误模型。
关键原则:
客户端应该依赖稳定语义,而不是解析随机错误字符串。
6. Authentication / Authorization
契约需要说明:
- 谁可以调用;
- credential 放在哪里;
- credential scope;
- 哪些错误表示 unauthenticated / forbidden;
- resource-level authorization 的边界。
详见 API 认证与安全。
7. Idempotency / Retry Semantics
特别是写操作:
- 请求能否安全重试;
- 重复提交如何识别;
- 是否支持 idempotency key;
- timeout 后客户端如何判断“没执行”还是“执行了但响应丢失”。
详见 接口幂等设计。
8. Version / Compatibility
需要明确:
- 哪些修改是 backward compatible;
- 字段新增是否安全;
- 删除/重命名怎么迁移;
- breaking change 如何版本化。
详见 API 版本管理。
文档不是 Contract 的唯一载体
同一契约可以同时投影到:
OpenAPI
Type definitions
Server validators
Client SDK
Mock server
Integration tests
Human documentation
理想状态是:
这些产物尽量来自同一权威契约,而不是各维护一份。
否则就会出现:
文档说 A
后端实现 B
前端类型写 C
Mock 返回 D
Contract 与实现的边界
API Contract 只承诺调用方需要知道的稳定外部行为。
它不应该暴露:
- 内部数据库表结构;
- 内部异常堆栈;
- 服务内部类名;
- 可随时变化的实现细节。
例如:
HTTP 409 + USERNAME_ALREADY_EXISTS
可以是稳定契约;
而:
PrismaUniqueConstraintException at UserRepository.ts:82
属于内部实现信息。
Content-Type 是契约的一部分
常见例子:
| Content-Type | 典型用途 |
|---|---|
application/json | 结构化 API 数据 |
multipart/form-data | 文件 + 表单字段 |
application/x-www-form-urlencoded | 传统表单 |
text/plain | 纯文本 |
application/octet-stream | 原始二进制 |
选择取决于数据语义,不存在“所有 API 都应该用 JSON”。
Contract-first 与 Code-first
Contract-first
先定义 API Contract
→ review
→ server / client / mock / test 实现
适合:
- 多团队协作;
- API 先行设计;
- 外部开放 API;
- 前后端并行开发。
Code-first
从 server type / annotation / route
→ 生成 spec
适合已有代码驱动开发流。
真正重要的不是名称,而是:
最终是否有一个稳定权威来源,并能检测实现漂移。
最小 API Review 清单
设计一个接口时至少检查:
- endpoint / operation 语义明确;
- input 有类型与 validation;
- success response 明确;
- error model 稳定;
- auth / authorization 明确;
- retry / idempotency 明确;
- pagination / ordering 明确(如果有集合);
- version / compatibility 已考虑;
- 有 contract / integration test;
- 文档与实现来自同一事实来源或有 drift check。
常见误区
“有 Swagger 页面就有接口规范”
Swagger UI 只是展示/交互工具。
真正的契约是里面定义的 operation、schema 与行为语义是否正确并与实现一致。
“所有响应必须套 success/data/message”
这是某些项目的 envelope convention,不是通用 API 原理。
“只要 TS 类型一样就安全”
TypeScript 类型不验证网络上的 runtime data。
边界仍需要 runtime validation / server validation。
“状态码够了,不需要 error code”
状态码表达大类;稳定业务错误往往还需要机器可读 code。
学习检查
不看正文回答:
- 为什么 API Contract 不等于一页接口文档?
- 一个完整契约除了 path/method,还至少需要哪些行为维度?
- 为什么
{ success, data, message }不是通用 API 规范本身? - HTTP status 与稳定业务 error code 各自解决什么?
- TypeScript interface 为什么不能替代网络边界的 runtime validation?
- Contract-first 与 code-first 真正需要保证的共同目标是什么?
- 为什么数据库 schema、内部 stack trace 不应该直接成为外部 API Contract?
- 给一个
POST /orders,从 input、output、error、auth、idempotency、version 六层写出最小契约。