面试追问地图

主问题必讲关键点下一层追问
REST资源、Method、状态码、无状态PUT/PATCH、复杂动作如何表达
错误码稳定、分层、traceIdHTTP 码与业务码如何配合
分页offset vs cursor、稳定排序数据插入导致翻页重复/遗漏
幂等稳定 Key、请求摘要、结果复用并发首请求、记录过期
乐观锁version/ETag、条件更新冲突后如何合并
版本管理向后兼容、弃用流程枚举新增、字段删除
API 签名规范化请求、时间戳、Nonce参数顺序、Body 摘要、密钥轮换
防重放时间窗口、Nonce、幂等多机如何共享 Nonce
限流维度、算法、返回语义Retry-After、分布式限流
超时总预算、逐层递减重试如何落入预算
OpenAPI契约、生成、CI 检查文档与实现漂移

接口题要从调用方视角回答:失败是否可重试、重复请求会怎样、字段变化是否兼容。


一、REST 与资源设计

什么是 RESTful API?

难度 🟡

REST 强调以资源为中心,使用统一接口表达操作:

  • URI 表示资源。
  • HTTP Method 表示动作。
  • 状态码表达结果。
  • 请求之间尽量无状态。

例如:

POST   /orders
GET    /orders/{id}
PATCH  /orders/{id}
DELETE /orders/{id}

REST 是设计风格,不是强制所有接口都只能按 CRUD 表达。

PUT 和 PATCH 的区别?

  • PUT 通常表示完整替换,语义应幂等。
  • PATCH 表示部分更新,是否幂等取决于补丁语义。

接口文档必须明确未传字段是“不修改”还是“置空”。

API 应该返回统一 200 吗?

不建议把所有结果都包装成 HTTP 200。应同时使用:

  • HTTP 状态码表示协议层结果。
  • 业务错误码表示具体业务原因。

例如参数错误使用 400,未认证使用 401,无权限使用 403,资源不存在使用 404。


二、请求与响应契约

如何设计稳定的错误码?

错误码应:

  • 稳定且机器可判断。
  • 与展示文案分离。
  • 包含业务域和错误类型。
  • 文档化并避免复用旧含义。

响应可包含:

code + message + details + traceId

不要把内部异常栈和数据库信息直接返回客户端。

分页接口如何设计?

  • 页码分页:简单,适合浅分页和稳定性要求低的后台页面。
  • 游标分页:基于稳定排序键,适合大数据和连续翻页。

游标应包含排序位置,不应只把数据库自增 ID 当作所有排序场景的通用游标。

时间和金额字段如何设计?

  • 时间明确时区和精度,跨系统推荐 ISO 8601 或时间戳并约定含义。
  • 金额使用最小货币单位整数或明确精度的 Decimal。
  • 不使用浮点数承载金额。

空值、缺失字段和空集合如何区分?

  • 字段缺失:可能表示未提供或保持不变。
  • null:明确无值。
  • 空数组:集合存在但没有元素。

契约必须稳定,否则客户端很容易出现兼容问题。


三、幂等与并发控制

创建接口如何保证幂等?

难度 🔴

客户端提交稳定的 Idempotency-Key,服务端保存:

幂等键 + 请求摘要 + 处理状态 + 最终响应

重复请求:

  • 请求内容一致时返回原结果。
  • 请求内容不一致时拒绝复用同一个键。

数据库唯一约束应作为最终防线。

通用概念:幂等键+请求摘要+结果表是幂等的第三层;能把相对操作改写成绝对操作就别上幂等键。

如何防止重复提交?

  • 前端按钮防抖只能改善体验。
  • 服务端幂等键。
  • 业务唯一索引。
  • 状态机条件更新。
  • 消息消费幂等。

不能只依赖 Redis 短期锁,否则锁过期后仍可能重复写入。

乐观锁如何用于接口更新?

资源携带版本号,更新时:

UPDATE resource
SET ..., version = version + 1
WHERE id = ? AND version = ?

更新行数为 0 表示版本冲突,客户端需要重新读取或提示用户。

HTTP 也可使用 ETag 与 If-Match 表达条件更新。


四、接口版本与兼容性

API 如何做版本管理?

常见方式:

  • URL:/v1/orders
  • Header。
  • 媒体类型。

比版本号形式更重要的是兼容策略:优先做向后兼容的字段扩展,重大语义变化才升级版本。

如何保证向后兼容?

  • 新增可选字段,不随意删除或重命名。
  • 枚举新增值时客户端应有未知值兜底。
  • 不改变已有字段类型和语义。
  • 服务端忽略无法识别的扩展字段。
  • 先发布兼容代码,再迁移数据和调用方。

如何下线旧接口?

  1. 统计调用方和流量。
  2. 通知迁移期限。
  3. 提供兼容层和迁移文档。
  4. 告警旧版本调用。
  5. 逐步限流或灰度关闭。
  6. 确认无流量后删除。

五、鉴权、签名与防重放

API 鉴权通常放在哪些层?

  • 网关做统一身份校验和粗粒度权限。
  • 服务内部做业务权限和数据归属判断。
  • 数据层使用最小权限账号。

不能因为网关已鉴权就省略服务内的越权校验。

开放 API 如何设计签名?

常见签名输入:

HTTP Method + Path + 规范化参数
+ Body 摘要 + Timestamp + Nonce

服务端使用客户端 Secret 验证签名,并检查时间窗口和 Nonce。

如何防止重放攻击?

  • 请求包含时间戳。
  • 限制允许时间窗口。
  • Nonce 在窗口内只能使用一次。
  • 签名覆盖请求关键内容。
  • 对业务写操作仍做幂等。

HTTPS 防窃听和篡改,但不能单独防止合法请求被重复发送。


六、限流与服务保护

API 限流维度有哪些?

  • 全局。
  • 应用/AppKey。
  • 用户。
  • IP 或设备。
  • 接口。
  • 业务资源。

限流 Key 设计不当可能导致热点或绕过。

被限流后应该返回什么?

  • HTTP 429。
  • 稳定错误码。
  • 必要时返回 Retry-After
  • 写接口明确请求是否已受理。

客户端应退避重试,不能立即高频重发。

如何设计 API 超时?

总超时应从用户请求预算向下游逐层分配:

客户端超时 > 网关超时 > 服务调用总预算

下游单次超时与重试次数必须落在总预算内,避免上层已断开后下游仍持续执行。


七、文档与治理

OpenAPI 有什么作用?

  • 描述接口路径、参数、响应和 Schema。
  • 生成交互文档、客户端代码和 Mock。
  • 在 CI 中检查契约变更。

自动生成文档不代表文档质量高,仍需补充业务语义、错误码和示例。

如何治理接口变更?

  • Schema 兼容性检查。
  • 消费者契约测试。
  • 变更评审和负责人。
  • 调用链、错误率和版本流量监控。
  • 建立弃用和下线流程。

日志中应该记录哪些 API 信息?

  • Trace ID、调用方、接口和版本。
  • 状态码、业务错误码、耗时。
  • 下游调用结果。
  • 必要的业务标识。

对密码、Token、身份证、手机号等敏感信息脱敏,不记录完整请求体作为默认做法。