面试追问地图
| 主问题 | 必讲关键点 | 下一层追问 |
|---|---|---|
| REST | 资源、Method、状态码、无状态 | PUT/PATCH、复杂动作如何表达 |
| 错误码 | 稳定、分层、traceId | HTTP 码与业务码如何配合 |
| 分页 | 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。
- 媒体类型。
比版本号形式更重要的是兼容策略:优先做向后兼容的字段扩展,重大语义变化才升级版本。
如何保证向后兼容?
- 新增可选字段,不随意删除或重命名。
- 枚举新增值时客户端应有未知值兜底。
- 不改变已有字段类型和语义。
- 服务端忽略无法识别的扩展字段。
- 先发布兼容代码,再迁移数据和调用方。
如何下线旧接口?
- 统计调用方和流量。
- 通知迁移期限。
- 提供兼容层和迁移文档。
- 告警旧版本调用。
- 逐步限流或灰度关闭。
- 确认无流量后删除。
五、鉴权、签名与防重放
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、身份证、手机号等敏感信息脱敏,不记录完整请求体作为默认做法。