API 设计与 HTTP 实践
后端对外(或对内)的第一界面是 API。面试很少考「REST 定义背全文」,更多考:资源怎么建模、HTTP 语义怎么用、幂等与错误怎么设计。
REST 直觉
| 原则 | 做法 |
|---|---|
| 资源用名词 | /users、/orders/{id},动词放 HTTP 方法 |
| 方法表语义 | GET 读、POST 建、PUT/PATCH 改、DELETE 删 |
| 状态码表结果 | 2xx 成功、4xx 客户端、5xx 服务端 |
| 无状态 | 服务端不存会话上下文在「连接」里(鉴权 token 除外) |
GET /api/v1/orders?status=paid&page=2
POST /api/v1/orders
PATCH /api/v1/orders/42 { "status": "shipped" }
DELETE /api/v1/orders/42
HTTP 状态码(常用)
| 码 | 何时用 |
|---|---|
| 200 | GET/PATCH 成功 |
| 201 | POST 创建成功 |
| 204 | DELETE 成功无 body |
| 400 | 参数非法 |
| 401 | 未认证 |
| 403 | 已认证无权限 |
| 404 | 资源不存在 |
| 409 | 冲突(重复创建、版本冲突) |
| 429 | 限流 |
| 500 | 未预期服务端错误 |
别全 200 + body 里 code——网关、监控、客户端库都依赖真实状态码。
版本与兼容
| 策略 | 说明 |
|---|---|
| URL 版本 | /v1/users — 最直观 |
| Header | Accept: application/vnd.xxx.v2+json |
| 字段扩展 | 新增可选字段;删/改语义要新版本 |
破坏性变更:新版本 + deprecate 旧版 + 文档与下线时间。
分页
| 方式 | 优缺点 |
|---|---|
| Offset | ?page=2&size=20;深页慢(DB OFFSET) |
| Cursor / Keyset | ?after=id123&limit=20;稳定、适合 Feed |
| 时间游标 | 按 created_at + id tie-break |
对外 API 推荐 cursor;管理后台可用 offset。
幂等
| 场景 | 做法 |
|---|---|
| GET / PUT / DELETE | 天然幂等(语义上) |
| POST 创建 | Idempotency-Key 请求头 + 服务端去重表 |
| 支付 / 下单 | 业务唯一键 + DB 唯一约束 |
重复请求应返回同一结果,而不是重复扣款。
错误体
{
"error": {
"code": "ORDER_NOT_FOUND",
"message": "订单不存在",
"request_id": "req-abc-123"
}
}
| 字段 | 作用 |
|---|---|
code | 机器可读,客户端分支 |
message | 人可读(勿泄露栈) |
request_id | 链路追踪 |
REST vs RPC / gRPC
| REST + JSON | gRPC | |
|---|---|---|
| 协议 | HTTP/1.1 或 HTTP/2 | HTTP/2 |
| 契约 | OpenAPI 常见 | Protobuf |
| 场景 | 对外 API、浏览器 | 内部微服务、高性能 |
| 流式 | SSE / WebSocket 另说 | 原生 streaming |
选型:对外 REST/OpenAPI;内部高频调用 gRPC 很常见。
与认证的关系
401/403 边界、Token 放 Header 见 认证与授权。网关层统一鉴权与路由见 API 网关设计与实现。
常见误解
- 「REST 不能有任何 RPC 味道」—— pragmatic REST 即可,别教条
- 「POST 一定不幂等」——可用幂等键设计幂等 POST
- 「版本写在 body 里就行」——网关与缓存难处理
面试速记
- RESTful 核心? 资源、HTTP 方法语义、状态码、无状态。
- 401 vs 403? 未登录 vs 无权限。
- 如何保证 POST 幂等? Idempotency-Key、业务唯一键。
- 深分页问题? OFFSET 大页慢;改 cursor/keyset。
- REST 和 gRPC 怎么选? 对外 vs 内部、契约、性能、浏览器支持。