API 设计规范

共 19 题
📑 题目列表 19 题
#
★★★

1. RESTful API 设计规范的核心约束中资源命名、HTTP 方法语义、状态码使用、版本化策略如何统一?

RESTful API 设计规范的核心约束:资源命名、HTTP 方法语义、状态码使用、版本化策略如何统一?

  • 资源命名(复数名词)
  • HTTP 方法语义(GET/POST/PUT/PATCH/DELETE)
  • 状态码使用

(1)资源命名:资源用复数名词(/users/orders),层级用 //orders/{id}/items),避免动词(动词应拆成子资源或动作)。 (2)HTTP 方法语义:GET(读,幂等)、POST(创建/动作)、PUT(全量替换,幂等)、PATCH(部分更新,幂等)、DELETE(删除,幂等)。方法语义与语义一致,避免"GET 改状态"。 (3)状态码使用:2xx(成功)、3xx(重定向)、4xx(客户端错误:400/401/403/404/409)、5xx(服务端错误)。状态码语义准确,配合错误体。 (4)版本化策略:URL 版本(/v1/users)、Header 版本(Accept: application/vnd.api+json;version=1)、内容协商。统一选一种并保证向后兼容。 (5)统一:把这些约束固化为规范文档 + OpenAPI lint + 评审 checklist,保证团队一致。

RESTful 核心约束是"资源名词 + 方法语义 + 状态码 + 版本化"。资源用复数名词、方法语义分明、状态码准确、版本化统一,用 OpenAPI lint + 评审强制。

# OpenAPI 示例
GET /v1/users/{id}          # 读用户
POST /v1/users              # 创建用户
DELETE /v1/users/{id}       # 删除用户
#
★★★

2. API 的向后兼容承诺与破坏性变更管理中字段废弃(deprecation)流程如何设计?

API 的向后兼容承诺与破坏性变更管理:字段废弃(deprecation)流程如何设计?

  • 向后兼容承诺(SemVer)
  • 字段废弃流程(deprecation)
  • 破坏性变更管理

(1)向后兼容承诺:API 遵循 SemVer——major 才允许破坏性变更,minor 只做向后兼容新增。承诺"不破坏已发布 API 的调用方"。 (2)字段废弃流程:a) 新增替代字段(保持旧字段);b) 旧字段标记 deprecated(文档/响应头/OpenAPI 标注);c) 通知调用方迁移;d) 在下一个 major 移除。旧字段至少保留一个 major 周期。 (3)破坏性变更管理:a) 破坏性变更(改字段名/类型/删除)必须 bump major;b) 提供迁移指南;c) 用 openapi-diff/breaking change 检测在 CI 拦截意外的破坏性变更;d) 用 deprecation 头(DeprecationSunset)提示。 (4)原则:向后兼容是默认承诺,破坏性变更走明确流程(deprecation → 迁移 → major 移除),用工具检测防止意外破坏。

向后兼容承诺靠 SemVer(major 才破坏)。字段废弃流程是"新增替代 → 标记 deprecated → 通知迁移 → major 移除",旧字段保留一个 major 周期。破坏性变更用 diff 检测 + deprecation 头管理。

# OpenAPI 废弃字段
users:
  type: object
  properties:
    userName: { type: string, deprecated: true }   # 旧字段
    loginName: { type: string }                     # 替代字段
#
★★★

3. RESTful API 的资源建模中嵌套资源、操作语义与版本策略如何设计?

RESTful API 的资源建模:嵌套资源、操作语义与版本策略如何设计?

  • 资源建模(集合、嵌套)
  • 操作语义(非 CRUD 动作)
  • 版本策略

(1)资源建模:资源用名词表示实体/集合,层级用嵌套(/orders/{id}/items)。嵌套资源表示"从属关系"(item 属于 order),避免过深嵌套(一般 ≤2 层)。 (2)操作语义:对非 CRUD 动作(审批、支付、状态变更),用动作资源(/orders/{id}/approve)或用方法(POST 到子资源);避免把动词塞进资源路径。操作语义要明确(是命令还是状态变更)。 (3)版本策略:统一版本化(URL /v1 或 Header),保证向后兼容;资源演进用字段新增(minor)与废弃(major)。 (4)建模原则:a) 资源表达"实体/集合",动作表达"过程";b) 嵌套表达从属,避免过度嵌套;c) 一致性(同一资源多端点风格统一)。

资源建模 = 名词资源 + 嵌套表达从属 + 动作资源表达过程。版本策略统一并向后兼容。关键是一致性——资源、嵌套、动作、版本风格统一,避免混乱。

# 嵌套资源:订单下的条目
GET /orders/{id}/items
GET /orders/{id}/items/{itemId}
# 动作资源:非 CRUD
POST /orders/{id}/approve
#
★★★

4. REST 与 RPC 风格混用的治理中同一系统内哪些接口保持资源式(REST)、哪些采用动作式(RPC endpoint),边界规范如何防止风格漂移与团队认知分裂?

REST 与 RPC 风格混用的治理:同一系统内哪些接口保持资源式(REST)、哪些采用动作式(RPC endpoint)?边界规范如何防止风格漂移与团队认知分裂?

  • REST 与 RPC 的适用场景
  • 风格混用的边界
  • 防止风格漂移与认知分裂

(1)REST 适用:资源型、CRUD、可建模为实体/集合的接口(订单、用户、商品)。RPC 适用:动作型、命令型、跨服务调用(结算、审批、内部服务 RPC)。 (2)边界划分:a) 面向外部/资源语义 → REST;b) 面向内部/动作/命令 → RPC(gRPC、内部 endpoint);c) 同一系统内明确"哪些资源用 REST、哪些动作用 RPC",避免同一种接口两种风格。 (3)防漂移:a) 规范明确边界(REST 用于资源、RPC 用于内部动作);b) 评审 checklist 检查"新接口是否选对风格";c) OpenAPI lint 检查资源命名(避免动词);d) 契约测试维护风格一致。 (4)防认知分裂:a) 风格决策集中(架构评审),避免各团队自行其是;b) 文档统一说明"REST vs RPC 边界";c) 同一资源不要既 REST 又 RPC 双实现。

REST 与 RPC 混用要"边界清晰"。REST 用于资源型面向外部,RPC 用于动作型内部调用。边界用规范 + 评审 + lint 防止漂移,风格决策集中防止团队认知分裂。

# REST(资源):面向外部
GET /v1/users/{id}
# RPC(动作):内部调用
POST /internal/settle    # 内部结算动作
#
★★

5. API 版本管理(URL/Header/内容协商三种)的取舍与向后兼容策略?

API 版本管理(URL/Header/内容协商三种)的取舍与向后兼容策略?

  • URL / Header / 内容协商三种版本化
  • 各方式取舍
  • 向后兼容策略

(1)URL 版本:/v1/users。优点:显式、易缓存、易调试、幂等;缺点:URL 膨胀、跨版本共享资源困难。最适合外部、公共 API。 (2)Header 版本:Accept: application/vnd.api+json;version=1 或自定义 X-API-Version。优点:URL 干净、演进灵活;缺点:不显式、调试难、缓存/网关处理复杂。适合内部、需要细粒度演进。 (3)内容协商:Accept: application/vnd.myapp.v2+json。优点:媒体类型语义化;缺点:复杂、客户端实现成本高。适合强媒体类型绑定场景。 (4)取舍:公共 API 常用 URL 版本(显式);内部 API 常用 Header 或内容协商(灵活)。关键选一种统一。 (5)向后兼容:无论哪种版本,都遵循"minor 兼容新增、major 才破坏";旧版本保持可用,走 deprecation 再淘汰。

三种版本化各有取舍:URL 显式易用(公共 API)、Header 灵活 URL 干净(内部)、内容协商语义化(复杂)。选一种统一,并遵循 SemVer 向后兼容(旧版本保留,deprecation 淘汰)。

# URL 版本
GET /v1/users
# Header 版本
Accept: application/vnd.api+json;version=1
# 内容协商
Accept: application/vnd.myapp.v2+json
#
★★

6. API 幂等性设计规范中哪些接口必须幂等?幂等键如何约定?

API 幂等性设计规范:哪些接口必须幂等?幂等键如何约定?

  • 幂等接口(GET/PUT/DELETE 天然幂等,POST 需设计)
  • 幂等键(Idempotency-Key)
  • 幂等实现

(1)必须幂等:a) GET(读,天然幂等);b) PUT/DELETE(替换/删除,天然幂等);c) POST(创建/动作,非幂等,需设计幂等)——尤其是支付、下单、通知等可能重复请求的接口。 (2)幂等键约定:客户端发送 Idempotency-Key 头(UUID),服务端以键为唯一标识,重复请求返回首次结果;键在窗口期内有效(如 24h)。 (3)实现:a) 服务端用幂等键做唯一索引/去重;b) 处理前检查"是否已处理过该键";c) 并发用唯一约束/分布式锁防重复;d) 返回首次结果(重放)。 (4)规范:a) 幂等接口在 OpenAPI 标注;b) 定义幂等键的生成、有效期、冲突处理;c) 非幂等接口(纯查询)无需键。

幂等性:GET/PUT/DELETE 天然幂等,POST(创建/动作)需用 Idempotency-Key 设计。客户端发幂等键,服务端去重并返回首次结果,防止重复支付/下单。用唯一约束 + 分布式锁防并发重复。

POST /v1/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
# 服务端:以键去重,重复请求返回首次结果
#
★★

7. 分页、排序、过滤参数的统一约定与游标分页 vs 偏移分页的规范选择?

分页、排序、过滤参数的统一约定,以及游标分页 vs 偏移分页的规范选择?

  • 分页/排序/过滤参数约定
  • 游标分页 vs 偏移分页
  • 规范选择

(1)分页参数:page/size(偏移分页)或 cursor/limit(游标分页);统一命名(pagesizecursorlimit)与响应(datapagination)。 (2)排序:sort 参数(sort=createdAt,-amount- 表示降序),白名单校验字段防注入。 (3)过滤:filter 或查询参数(status=active),统一语法与操作符。 (4)游标 vs 偏移:a) 偏移分页(OFFSET/LIMIT)——简单、可跳页,但大数据集时深度分页性能差、数据变动时重复/遗漏;b) 游标分页(keyset,基于 createdAt/id)——稳定、高效,适合大数据、实时追加场景,但难跳页。 (5)选择:中小数据、需跳页用偏移;大数据、实时、稳定分页用游标。规范统一参数与响应格式。

分页/排序/过滤参数统一约定(page/size/cursor/limit、sort、filter)。游标分页(keyset)稳定高效适合大数据,偏移分页(OFFSET)简单适合中小数据,按数据规模与场景选择并统一。

// 游标分页响应
{ "data": [...], "pagination": { "nextCursor": "eyJpZCI6MTAwfQ", "hasMore": true } }
// 偏移分页
GET /v1/orders?page=2&size=20&sort=-createdAt&status=active
#
★★

8. API 错误模型中错误码、错误消息、字段级错误(field errors)与重试头(Retry-After)的规范化设计?

API 错误模型:错误码、错误消息、字段级错误(field errors)与重试头(Retry-After)如何规范化设计?

  • 错误模型结构(错误码、消息、字段级错误)
  • 字段级错误(field errors)
  • 重试头(Retry-After)

(1)错误模型结构:统一错误响应体 { code, message, ... },含错误码、可读消息、可选字段级错误、traceId。前端据 code 分支。 (2)字段级错误:表单校验错误时返回 fieldErrors: [{ field, code, message }],前端按字段定位错误,逐字段展示。 (3)重试头:限流/过载时返回 Retry-After(秒或日期),告知客户端何时可重试;配合 429 Too Many Requests。 (4)规范化:a) 错误响应结构统一(全局异常处理器保证);b) 错误码字典集中(统一错误码表);c) 字段级错误与业务错误区分;d) Retry-After 与可重试错误对应。

API 错误模型 = 统一错误体(code/message)+ 字段级错误(fieldErrors 供表单定位)+ Retry-After(限流重试提示)。错误码字典集中、结构统一,前端可稳定处理。

{
  "code": "B3001",
  "message": "参数校验失败",
  "fieldErrors": [
    { "field": "email", "code": "INVALID_FORMAT", "message": "邮箱格式错误" }
  ],
  "traceId": "abc"
}
// 限流响应
HTTP/1.1 429 Too Many Requests
Retry-After: 30
#
★★

9. GraphQL 与 REST 的 API 设计取舍,schema 演进与向后兼容如何保障?

GraphQL 与 REST 的 API 设计取舍?schema 演进与向后兼容如何保障?

  • GraphQL vs REST 取舍
  • schema 演进
  • 向后兼容保障

(1)GraphQL 取舍:优点——单端点、客户端按需取字段、强类型 schema、减少过度/不足获取;缺点——缓存难、查询复杂度控制难、工具链复杂。REST 优点——缓存、语义清晰、工具成熟;缺点——过度/不足获取、多端点。 (2)取舍依据:数据形状多样、客户端多变 → GraphQL;资源语义清晰、需缓存/公共 API → REST。混合(REST 面向资源 + GraphQL 面向复杂查询)也可。 (3)schema 演进:GraphQL schema 需向后兼容——新增字段/类型是兼容的,删除/改字段是破坏性的。遵循"只加不改"。 (4)向后兼容保障:a) 用 schema registry / snapshot 校验(新增不破坏,删除需 major);b) 废弃字段用 @deprecated 标注而非删除;c) 契约测试 + breaking change 检测;d) schema 版本化与文档化。

GraphQL 适合按需取数/复杂查询,REST 适合资源语义/缓存。schema 演进遵循"只加不改",废弃用 @deprecated,删除要 major。用 schema registry + diff 检测保障向后兼容。

# schema 演进:新增字段兼容,废弃标注
type User {
  id: ID!
  name: String!
  loginName: String   # 新增,兼容
  oldName: String @deprecated(reason: "use loginName")
}
#
★★

10. API 乐观并发控制的规范选择中 ETag/If-Match、版本号字段、Last-Modified 三种机制的适用场景与团队统一约定如何制定?

API 乐观并发控制的规范选择:ETag/If-Match、版本号字段、Last-Modified 三种机制的适用场景与团队统一约定?

  • ETag/If-Match、版本号、Last-Modified 三种机制
  • 适用场景
  • 团队统一约定

(1)ETag/If-Match:服务端返回 ETag(资源哈希),客户端更新时带 If-Match: <etag>,服务端校验不匹配返回 412。HTTP 标准、无侵入,适合 REST。 (2)版本号字段:资源带 version 字段,更新时客户端带上版本号,服务端 WHERE version=? 比对,冲突返回 409。简单、显式、便于业务,适合业务字段版本。 (3)Last-Modified/If-Modified-Since:基于时间戳,粒度粗(秒级),适合缓存类,不适合高并发精确并发控制。 (4)适用场景与统一:a) 高并发、精确、HTTP 标准 → ETag/If-Match;b) 业务需要显式版本/乐观锁 → 版本号字段 + 409;c) 粗粒度缓存 → Last-Modified。团队统一选一种(如 ETag 或版本号),在规范中约定响应头与冲突状态码。

三种乐观并发机制:ETag/If-Match(HTTP 标准,412)、版本号字段(显式业务,409)、Last-Modified(粗粒度缓存)。团队统一选一种(ETag 或版本号),约定响应头与冲突状态码。

# ETag
GET /v1/users/1 → ETag: "abc123"
PUT /v1/users/1
If-Match: "abc123"   # 不匹配返回 412
# 版本号
PUT /v1/users/1
{ "version": 3, "name": "x" }   # 冲突返回 409
#
★★

11. API 规范在代码评审中的落地中如何把 API 设计规范固化为评审 checklist、契约测试与自动化校验(OpenAPI lint、breaking change 检测)?

如何把 API 设计规范固化为评审 checklist、契约测试与自动化校验(OpenAPI lint、breaking change 检测),让 API 规范在代码评审中真正落地?

  • API 设计规范从文档到可执行机制的固化
  • 评审 checklist、契约测试与自动化校验(OpenAPI lint、breaking change 检测)
  • 规范落地与 CI 门禁的结合

(1)规范固化的三层手段:评审 checklist(人工)、契约测试(行为)、自动化校验(OpenAPI lint / breaking change 检测,机器)。规范从'建议'变成'门禁',靠的是把每个条目落到可执行检点。

(2)评审 checklist:把规范(资源命名、状态码、字段命名、幂等、分页)做成 reviewer 可勾选的清单,如'资源用名词复数?''错误模型是否统一?''是否声明幂等键?'。清单要分层(通用项 + API 专项),避免过长导致逐项勾选失效。

(3)契约测试:用 Pact / Spring Cloud Contract 对 API 契约做测试,锁定请求/响应结构,防止提供方破坏消费方预期的契约。契约测试纳入 CI,任一变更导致契约不匹配即失败。

(4)自动化校验:a) OpenAPI lint(如 spectral、redocly lint)校验规范(操作语义、命名、响应码、安全);b) breaking change 检测(如 openapi-diff、oasdiff)在版本变更时自动发现破坏性变更并阻断;c) 把以上接入 CI 门禁,构成 PR 的强制检查。

API 规范落地靠'人工 checklist + 契约测试 + 自动化校验'三层协同。人工处理机器无法判断的语义,契约测试守护行为,OpenAPI lint 与 breaking change 检测把规范变成机器可执行的 CI 门禁,三者缺一不可。

# CI 门禁示例

- run: spectral lint openapi.yaml   # OpenAPI 规范 lint

- run: oasdiff breaking openapi_v1.yaml openapi_v2.yaml  # breaking change 检测

- run: pact-verifier                 # 契约测试

#
★★

12. API 字段设计规范中命名(snake_case vs camelCase)、可空性、枚举扩展性与时间/金额表示(ISO 8601、最小货币单位)如何统一?

API 字段设计规范中,命名(snake_case vs camelCase)、可空性、枚举扩展性、时间/金额表示(ISO 8601、最小货币单位)如何统一制定?

  • 字段命名风格统一(snake_case vs camelCase)
  • 可空性与枚举扩展性
  • 时间(ISO 8601)与金额(最小货币单位)的表示规范

(1)命名:全仓统一一种风格(推荐 snake_case 或按团队语言习惯),用契约规范(OpenAPI 命名约束)与 lint 强制,避免混用。命名应表达字段语义而非实现。

(2)可空性:明确字段可空性,用 OpenAPI 的 nullable/required 表达;区分'缺失'与'空值',非空字段与可空字段在契约中显式声明,避免调用方误判。

(3)枚举扩展性:枚举字段用字符串/符号而非数字,预留扩展空间;说明'未知枚举值'的处理策略(保留并告警 vs 拒绝),避免新增枚举值破坏存量调用方。

(4)时间表示:统一 ISO 8601(如 2026-08-04T12:00:00Z),用带时区(UTC)的格式,避免本地时间歧义;金额用最小货币单位(分/cent)整数或带精度的十进制字符串,禁用浮点,避免精度损失。

API 字段规范的核心是'契约确定性'。命名统一、可空性显式、枚举可扩展、时间用 ISO 8601、金额用最小货币单位,这些都是消除跨语言/跨团队歧义的关键。规范要用契约 lint 与约定文档固化。

# OpenAPI 字段规范示例

priceCents:

  type: integer

  description: 价格,以最小货币单位(分)表示,禁止浮点

createdAt:

  type: string

  format: date-time

  description: ISO 8601 UTC 时间

status:

  type: string

  enum: [PENDING, ACTIVE, CANCELLED]

  description: 枚举,新增值需评估向后兼容

#
★★

13. API 的一致性治理中如何维护 API 清单与所有权(owners),跨团队新增 API 的评审与注册流程如何设计?

API 的一致性治理中,如何维护 API 清单与所有权(owners)?跨团队新增 API 的评审与注册流程如何设计?

  • API 清单(catalog)与所有权(owners)的维护
  • 跨团队新增 API 的评审与注册流程
  • 防止 API 重复、冲突与无人维护

(1)API 清单:建立统一的 API 目录(catalog),记录每个 API 的路径、负责人、版本、依赖、状态。用工具(如 Backstage、Swagger Hub、自建 registry)自动采集与维护,避免手工清单过期。

(2)所有权(owners):每个 API 必须有明确的 owner(团队/个人),维护该 API 的演进、兼容与告警责任。用 CODEOWNERS-like 机制与 owner 字段,防止'无人维护'的孤儿 API。

(3)新增 API 评审流程:跨团队新增 API 时,先提交 API 提案(含契约、命名、幂等、版本策略),经 API 治理小组评审(检查命名冲突、风格一致性、是否与现有 API 重复),通过后注册到清单。

(4)注册与门禁:评审通过后,API 契约(OpenAPI)纳入统一 registry,CI 校验新 API 是否有 owner、是否通过命名/风格约束、是否登记。这样新增 API 从源头保持一致,避免团队各自为政。

API 一致性治理 = 清单(记录)+ 所有权(责任)+ 评审注册(流程)+ 门禁(强制)。清单防重复、所有权防孤儿、评审把一致性前置、门禁把流程固化。四个环节共同防止 API 失控。

# API 注册条目示例

api:

  path: /v1/orders/{id}

  owner: team-order

  status: stable

  version: 1.0.0

  contract: openapi.yaml

#
★★

14. 版本化策略与淘汰中 API 生命周期(预览→正式→弃用→移除)的窗口与通知机制,废弃字段如何通过响应头与文档提示迁移?

API 生命周期(预览→正式→弃用→移除)的窗口与通知机制如何设计?废弃字段如何通过响应头与文档提示迁移?

  • API 生命周期四个阶段(预览→正式→弃用→移除)
  • 各阶段窗口与通知机制
  • 废弃字段通过响应头与文档提示迁移

(1)生命周期阶段:预览(preview,不承诺稳定)→ 正式(stable,承诺向后兼容)→ 弃用(deprecated,仍可用但提示迁移)→ 移除(removed,不再可用)。每个阶段有明确的窗口期与通知。

(2)窗口与通知:弃用阶段保留足够时间(如 6-12 个月),提前在文档公告、release notes 通知调用方;移除前再次提醒,尊重调用方迁移节奏。

(3)废弃字段提示:通过响应头 DeprecationSunset(在 HTTP 规范中表达废弃与移除时间)提示调用方;文档中标注 deprecated 字段并给出替代方案。

(4)机制配套:a) 用 Deprecation 头标记废弃字段,Sunset 头告知移除时间;b) 文档标注替代字段;c) 监控废弃字段的调用量,归零后再移除。

API 生命周期管理的关键是'透明与缓冲'。阶段明确、窗口合理、通知提前,让调用方有时间迁移。废弃字段用 Deprecation/Sunset 响应头 + 文档提示,配合调用量监控,实现平滑淘汰。

HTTP/1.1 200 OK

Deprecation: true

Sunset: Sat, 04 Aug 2027 00:00:00 GMT

Link: <https://api.example.com/v2/orders>; rel="deprecation"

#
★★

15. API 可观测性规范中请求 ID、结构化错误与调用方标识如何作为 API 规范的一部分,支撑链路追踪与服务商责任界定?

API 可观测性规范中,请求 ID、结构化错误与调用方标识应如何作为 API 规范的一部分,支撑链路追踪与服务商责任界定?

  • 请求 ID(request ID)的注入与传递
  • 结构化错误响应的统一
  • 调用方标识(client ID)用于责任界定与追踪

(1)请求 ID:每个 API 请求应有唯一 request ID(由网关/服务生成,客户端可传入),贯穿日志与响应头,便于链路追踪与排障。响应头 X-Request-Id 返回给调用方。

(2)结构化错误:错误响应统一格式(如 { code, message, requestId, details }),错误码分段定义,便于调用方程序化处理与定位。

(3)调用方标识:客户端通过 API key / client ID 标识,服务端据此记录调用方、作用域与配额,支撑审计、责任界定与限流。

(4)规范落地:把这些字段(requestId、clientId、结构化错误)写入 API 规范(OpenAPI 的响应 schema、header 定义),作为硬性约定;配合 traceId 关联分布式追踪,实现跨服务全链路可观测。

API 可观测性把'追踪'与'责任'编码进规范。requestId 支撑链路追踪,clientId 支撑责任界定与审计,结构化错误支撑调用方处理。将这些作为契约的一部分,调用方与提供方都遵守,才能保证全链路可观测与责任清晰。

// 结构化错误响应

{

  "code": "ORDER_NOT_FOUND",

  "message": "订单不存在",

  "requestId": "req-12345",

  "details": { "orderId": "1001" }

}

#

16. OpenAPI 规范在 code-first vs design-first 团队的工程价值

OpenAPI 规范在 code-first 与 design-first 两种团队中分别有什么工程价值?如何取舍?

  • code-first(代码生成契约)与 design-first(契约驱动开发)
  • OpenAPI 在两套流程中的角色
  • 各团队的取舍与工程价值

(1)code-first:以代码为单一事实来源,用注解(如 springdoc)生成 OpenAPI。价值:与实现天然同步、开发快、适合内部快速迭代;风险:契约可能滞后于设计、schema 反映实现而非意图。

(2)design-first:以 OpenAPI 契约为单一事实来源,先生成契约再生成代码/文档。价值:契约先行、多方协作(前端/后端/第三方)可并行、契约稳定、可做评测与 mock;风险:代码与契约需同步维护。

(3)工程价值共通:无论哪种,OpenAPI 都提供机器可读的契约,支撑自动生成文档、客户端、mock、合约测试与 lint。

(4)取舍:团队有明确前端/后端协作或对外 API → design-first;快速迭代、内部 API → code-first。也可混合:对外契约 design-first,内部实现 code-first。核心是'确立单一事实来源'。

OpenAPI 的价值在于'契约机器可读'。code-first 适合快速迭代、实现驱动;design-first 适合契约稳定、多方协作。选型取决于团队协作方式与 API 稳定性要求,关键是单一事实来源。

# design-first:OpenAPI 契约

openapi: 3.0.0

paths:

  /orders/{id}:

    get:

      summary: 获取订单

      responses:

        '200':

          description: 成功

#

17. GraphQL/REST/tRPC 接口设计选择决策点

GraphQL、REST、tRPC 三种接口设计方式如何选择?各自的决策点是什么?

  • REST、GraphQL、tRPC 的特性差异
  • 各方案的适用场景
  • 选择决策点(类型安全、灵活性、迭代速度、多端)

(1)REST:资源式、语义清晰、缓存友好、生态成熟,适合对外公开 API 与第三方集成。缺点是字段/结构固定,多端场景需多次请求或过度获取。

(2)GraphQL:单端点、按需查询、客户端精确取字段,适合多端/移动端、字段灵活的场景。缺点是学习成本高、缓存和限流复杂、过度查询需治理。

(3)tRPC:端到端类型安全(TypeScript),函数式调用、零 schema 生成,适合全栈 TS 团队内部快速迭代。缺点是强绑定 TS 生态、跨语言/对外暴露受限。

(4)决策点:a) 是否对外公开 → REST/GraphQL;b) 是否全栈 TS、内部快速 → tRPC;c) 字段灵活性要求 → GraphQL;d) 缓存/生态成熟度 → REST。按团队技术栈、多端需求与暴露边界选择。

接口选型是'生态成熟度、类型安全、灵活性、多端支持'的权衡。REST 通用成熟、GraphQL 灵活按需、tRPC 类型安全快速。决策依据是团队栈、是否对外、字段灵活性、缓存需求。

// tRPC:端到端类型安全

const trpc = initTRPC.create()

export const appRouter = trpc.router({

  getUser: trpc.procedure

    .input(z.object({ id: z.string() }))

    .query(async ({ input }) => fetchUser(input.id)),

})

#

18. gRPC/Protobuf 的字段编号管理与 reserved 关键字在兼容性中的作用?

gRPC/Protobuf 中字段编号管理与 reserved 关键字对兼容性有什么作用?

  • Protobuf 字段编号的稳定性与兼容性
  • reserved 关键字(字段号/字段名)
  • 防止字段重用导致兼容性破坏

(1)字段编号:Protobuf 用整数编号(field number)标识字段,而非字段名。字段编号一旦发布就不可变,因为序列化依赖编号。删除字段时保留其编号,避免复用。

(2)reserved 关键字:用 reserved 5, 8; 保留已删除的字段编号,用 reserved "oldField"; 保留已删除的字段名,防止未来重新使用这些编号/名字导致新旧数据错位。

(3)兼容性作用:a) 删除字段后禁用其编号,防止未来新字段复用该编号造成旧数据反序列化错位;b) 字段名保留防止 JSON 映射冲突;c) 保证向前向后兼容(新增字段用新编号)。

(4)最佳实践:字段编号从 1 开始递增,预留扩展范围;不重用已删除字段的编号;单向兼容(只增不改)。

Protobuf 兼容性依赖字段编号的稳定性。reserved 关键字是'防复用'的保护机制,防止已删除字段的编号/名字被重新使用而破坏新旧数据一致性。这是 gRPC 契约演进的关键。

message Order {

  string id = 1;

  string customer_id = 2;

  reserved 3, 5;        // 保留已删除字段的编号

  reserved "legacy_field"; // 保留已删除字段名

  string status = 10;    // 新增字段用新编号

}

#

19. API 网关层的统一规范落地中鉴权、限流、CORS、请求 ID 注入如何作为 API 设计规范的一部分在网关层强制,与业务侧规范的边界在哪?

API 网关层的统一规范落地中,鉴权、限流、CORS、请求 ID 注入如何在网关层强制?与业务侧规范的边界在哪?

  • 网关层统一规范(鉴权、限流、CORS、请求 ID 注入)
  • 网关强制与业务侧规范的边界
  • 横切关注点下沉网关

(1)网关强制:鉴权(认证、token 校验)、限流(配额、突发控制)、CORS(跨域策略)、请求 ID 注入(统一生成 requestId)等横切关注点在网关层统一实现并强制,业务侧无需重复。

(2)边界划分:网关负责'传输层/横切'关注点(鉴权、限流、CORS、请求 ID、协议转换),业务侧负责'业务逻辑'(资源校验、业务规则、字段语义)。网关不处理业务规则,业务不重复做横切。

(3)规范固化:把网关统一策略写入 API 规范(如鉴权方式、限流阈值、CORS 白名单、请求 ID 头),作为全局约定;业务侧只需实现业务语义。

(4)边界注意:a) 网关鉴权是粗粒度(是否认证),业务侧细粒度授权(角色/资源)仍需业务实现;b) 网关限流是全局,业务侧可加局部配额;c) 避免网关过度变得'厚'而承担业务逻辑。

网关层落地横切规范(鉴权、限流、CORS、请求 ID)可减少业务重复、统一安全基线。边界是'横切 vs 业务':网关管传输层横切,业务管业务语义。粗粒度鉴权在网关、细粒度授权在业务,避免网关变厚。

# 网关配置示例

auth:

  type: jwt

  issuer: https://auth.example.com

rate_limit:

  rps: 100

cors:

  allowed_origins: [https://app.example.com]

request_id:

  header: X-Request-Id

  generate: true