← [ BACK_TO_NOTES ]

接口规范

接口规范(API Contract)定义调用双方稳定依赖的请求、响应、错误、安全、版本与行为语义,是实现、文档、测试、Mock 和客户端生成的共同边界。

#api #concept #evergreen

[!info] related notes

接口规范

一句话定义

接口规范是 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。

学习检查

不看正文回答:

  1. 为什么 API Contract 不等于一页接口文档?
  2. 一个完整契约除了 path/method,还至少需要哪些行为维度?
  3. 为什么 { success, data, message } 不是通用 API 规范本身?
  4. HTTP status 与稳定业务 error code 各自解决什么?
  5. TypeScript interface 为什么不能替代网络边界的 runtime validation?
  6. Contract-first 与 code-first 真正需要保证的共同目标是什么?
  7. 为什么数据库 schema、内部 stack trace 不应该直接成为外部 API Contract?
  8. 给一个 POST /orders,从 input、output、error、auth、idempotency、version 六层写出最小契约。
创建于 2025/1/1 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本