# API 设计要点：错误码、幂等、分页与限流

作者：星苒鸭（xingranya）
发布日期：2026-02-06
更新时间：2026-02-06
原文：https://blog.xran.uk/api-design-errors-idempotency-pagination/

API 设计这件事，最怕的是：功能能用，但细节“阴间”。例如：

- 出错只给一句 “error”，前端根本无法处理
- 客户端重试一次，订单下了两次
- 分页翻到一半数据乱跳，用户以为丢了

这篇不讲抽象原则，直接给你一套可落地的设计要点，适合大多数后端接口。

![接口就是网络上的“契约”，一旦乱了就很难改](https://images.unsplash.com/photo-1691435828932-911a7801adfb?auto=format&fit=crop&w=1600&q=80&ixlib=rb-4.1.0)

### 1) 错误格式：别只返回字符串

推荐用稳定结构：

```json
{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "用户不存在",
    "request_id": "req_01J..."
  }
}
```

建议你至少做到：

- **code 稳定**（客户端可写逻辑分支）
- **message 可读**（给人看的，别给机器解析）
- **request_id**（方便你在日志里一键定位）

### 2) HTTP 状态码：别全都 200

常用建议：

- 200：成功
- 201：创建成功
- 204：成功但无内容
- 400：参数/格式错误
- 401/403：认证/授权问题
- 404：资源不存在
- 409：冲突（比如重复创建）
- 429：限流
- 500：服务端未预料错误

> 重要：状态码解决“分类”，错误码解决“细分”。两者配合才舒服。

### 3) 幂等：让重试不会“重复扣款/重复下单”

幂等最常出现在：

- 支付/下单
- 发放权益
- 触发一次性任务

常见做法：**幂等键（Idempotency-Key）**。

- 客户端生成一个唯一 key（比如 UUID）
- 服务端把 key 与结果绑定一段时间
- 同一个 key 重复请求，直接返回第一次的结果

示意请求头：

```http
POST /api/orders
Idempotency-Key: 2b8e9d0d-...
```

### 4) 分页：Offset 简单，Cursor 更稳

#### Offset 分页（简单）

```http
GET /api/posts?page=3&page_size=20
```

问题：数据变化时可能“重复/漏掉”（尤其是按时间倒序时）。

#### Cursor 分页（推荐用于时间线/Feed）

```http
GET /api/posts?cursor=1700000000&page_size=20
```

响应建议返回：

```json
{
  "data": [...],
  "next_cursor": "1699999900",
  "has_more": true
}
```

好处：更稳定、更适合滚动加载。

### 5) 限流：保护自己，也保护用户体验

当你返回 429 时，建议带上：

- `Retry-After`：告诉客户端多久后重试更合适
- 统一的错误码（比如 `RATE_LIMITED`）

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 2
```

### 6) 一点点“工程化”的细节（很加分）

- **请求追踪**：每个请求都有 `request_id`，日志必打
- **一致性**：所有接口都遵循同一错误结构与分页结构
- **文档示例**：别只写字段说明，必须给完整请求/响应示例

### 总结

API 的好坏，本质是“契约质量”。你把错误、幂等、分页、限流这四件事打磨好，接口就会从“能用”变成“好用且可靠”。

> 封面与配图来自 Unsplash（免费使用授权）。
