统一响应
{
"code": "USER_NOT_FOUND",
"message": "用户不存在",
"traceId": "8f12a...",
"data": null
}优秀的 API 设计是后端系统的基石。从资源命名到错误处理,从认证鉴权到版本控制,系统化掌握 RESTful API 设计的最佳实践与原则,构建优雅可维护的 Web API。
REST(Representational State Transfer)是一种架构风格,而非协议。它利用 HTTP 协议的特性来设计 Web API,强调资源导向和统一接口。
使用名词复数表示资源集合,避免动词。层级关系用斜杠表示,查询参数用于过滤和排序。
| 方法 | 语义 | 幂等 | 安全 | 请求体 | 响应体 |
|---|---|---|---|---|---|
| GET | 获取资源 | ✅ | ✅ | 无 | 资源表示 |
| POST | 创建资源 | ❌ | ❌ | 创建内容 | 新资源 |
| PUT | 完整替换 | ✅ | ❌ | 完整资源 | 更新后资源 |
| PATCH | 部分更新 | 可设计 | ❌ | 差异字段 | 更新后资源 |
| DELETE | 删除资源 | ✅ | ❌ | 无 | 空/确认 |
推荐将版本号放在 URL 路径中(/api/v1/),便于浏览和调试。也可以通过请求头 Accept: application/vnd.example.v1+json 实现内容协商。
正确使用 HTTP 方法是 RESTful API 的核心。每种方法都有明确的语义、幂等性和安全性要求。
幂等且安全,不改变服务端状态。可被缓存。查询参数用于过滤、排序、分页。
非幂等,每次调用可能创建不同资源。返回 201 Created 及新资源 Location 头。
幂等,客户端提供完整的资源表示。缺失字段视为置空或默认值。返回 200 OK。
非幂等,只传递需要修改的字段。支持 JSON Patch (RFC 6902) 或 Merge Patch (RFC 7396)。
幂等,删除后后续 DELETE 返回 404。可返回 204 No Content 或 200 OK。
选择合适的 HTTP 状态码能让客户端准确理解请求结果。以下是常用的状态码及其使用场景。
200 OK — GET/PUT/PATCH 成功返回资源 201 Created — POST 创建成功,返回 Location 头 202 Accepted — 异步任务已接受,稍后处理 204 No Content — DELETE 成功,无返回体
301 Moved Permanently — 资源永久迁移 302 Found — 临时重定向 ⚠ 部分客户端会把 POST 降级为 GET 304 Not Modified — 缓存未过期 (ETag/If-None-Match) 307 Temporary Redirect — 临时重定向,保持 HTTP 方法
400 Bad Request — 请求格式错误 401 Unauthorized — 未认证,需要登录 403 Forbidden — 已认证但无权限 404 Not Found — 资源不存在 405 Method Not Allowed — 方法不允许 409 Conflict — 资源冲突(如重复创建) 422 Unprocessable Entity — 校验失败 429 Too Many Requests — 请求限流
500 Internal Server Error — 服务端内部错误 502 Bad Gateway — 网关/代理错误 503 Service Unavailable — 服务暂时不可用 504 Gateway Timeout — 网关超时
统一的错误响应格式让客户端能够一致地处理各种错误情况。建议遵循 RFC 7807 Problem Details 规范。
AUTH_TOKEN_EXPIREDAccept-Language 头切换trace_id 便于服务端排查分页是列表接口的基础能力。Offset 分页和 Cursor 分页各有适用场景,选择合适的方式能显著提升性能。
适用于:数据量不大(<10万条),支持跳页,按创建时间排序的场景。缺点:深分页性能差(OFFSET 偏移)。
适用于:大数据量、实时性高的场景。基于索引(主键/时间戳)定位,性能稳定。缺点:不支持随机跳页。
选择合适的认证方式对 API 安全性至关重要。以下是三种主流认证方案的对比和最佳实践。
无状态认证,token 包含用户信息和 claims。服务端无需存储 session,适合分布式架构。
授权框架,允许第三方应用获取有限的资源访问权限。支持授权码、客户端凭证等多种流程。
最简单的认证方式,适用于服务端到服务端、公共 API 或内部工具。通过 Header 或查询参数传递。
API 版本管理是保证向后兼容性的关键策略。不同的版本控制方式在可维护性和易用性上各有优劣。
优点:直观、易于调试、支持 CDN 缓存、适合大型公共 API。
缺点:URL 不够简洁、版本升级成本高。
优点:URL 干净、支持细粒度版本控制、适合媒体类型变化。
缺点:调试不便、工具兼容性差、对新手不友好。
Sunset: Thu, 31 Dec 2026 23:59:59 GMTDeprecation 头提示客户端即将弃用高质量的 API 文档是开发者体验的核心。使用 OpenAPI/Swagger 规范和自动化工具,让文档与代码同步。
交互式 API 文档浏览,支持在线调试
美观的静态 API 文档渲染
Spring Boot 集成 OpenAPI 自动生成
维护清晰的版本变更日志,标注 breaking changes
Rate Limiting 是保护 API 不被滥用的重要机制。通过标准化的响应头告知客户端当前的限流状态。
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 令牌桶 | 以固定速率添加令牌,请求消耗令牌 | 允许突发流量,实现简单 | 允许桶容量范围内突发 |
| 漏桶 | 请求进入队列,以固定速率处理 | 流量平滑,输出稳定 | 无法应对突发 |
| 固定窗口 | 时间窗口内计数,超限拒绝 | 实现最简单 | 窗口边界突发 |
| 滑动窗口 | 基于时间区间的精确计数 | 限流均匀,避免边界问题 | 实现较复杂 |
Richardson Maturity Model(RMM)定义了 REST API 的四个成熟度等级,帮助团队渐进式地提升 API 质量。
使用 HTTP 作为传输隧道,一个 URL 一个方法(通常是 POST),所有操作通过请求体区分。常见于早期 SOAP/XML-RPC。
引入资源概念,每个资源有独立 URL。但仍使用单一 HTTP 方法。
正确使用 HTTP 方法语义(GET/POST/PUT/DELETE),结合状态码表达结果。这是大多数 API 的目标级别。
响应中包含链接(links),客户端通过链接发现可执行的操作,实现超媒体驱动。
_links 或 links 字段包含相关资源链接{
"code": "USER_NOT_FOUND",
"message": "用户不存在",
"traceId": "8f12a...",
"data": null
}GET /api/orders?cursor=xxx&limit=20&sort=-createdAt&fields=id,status,totalPOST /api/payments
Idempotency-Key: order_1001_pay_1Deprecation: true
Sunset: Wed, 30 Dec 2026 23:59:59 GMTHTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Retry-After: 60{
"items": [],
"nextCursor": "eyJpZCI6MTAwMX0=",
"hasMore": true
}components:
schemas:
ErrorResponse:
required: [code, message, traceId]| 版本策略 | 示例 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| URL 路径版本 | /api/v1/users | 直观、可缓存、调试方便 | URL 冗余、版本粒度粗 | 公共 API、多客户端并存 |
| Header 内容协商 | Accept: application/vnd.myapp.v2+json | URL 干净、可细粒度演进 | 调试不便、工具兼容差 | 媒体类型频繁演进的中大型 API |
| 查询参数版本 | /api/users?version=2 | 实现最简单 | 易被缓存误处理、语义弱 | 仅限内部系统快速过渡 |
仅用于数据量可控的后台列表;需要展示总条数与跳页时使用。
基于索引定位,性能稳定;不暴露内部 ID 与总条数。
?status=active。?createdAt.gte=xxx&createdAt.lte=xxx。?status.in=a,b。?q= 单独承载,不与字段过滤混用。?sort= 单参数,逗号分隔多字段。- 表示倒序:?sort=-createdAt,id。HTTP 表达成败大类;业务 code 供客户端分支处理;message 面向用户展示;traceId 串联服务端日志。
| 业务码 | HTTP | 含义 | 客户端处理建议 |
|---|---|---|---|
| AUTH_TOKEN_EXPIRED | 401 | 访问令牌过期 | 静默刷新 Token 后重试 |
| PARAM_INVALID | 422 | 参数校验失败 | 按 errors 字段定位表单项 |
| ORDER_NOT_FOUND | 404 | 订单不存在 | 引导刷新列表 |
| ORDER_PAY_DUPLICATE | 409 | 重复支付 | 查询支付结果后收敛页面 |
| RATE_LIMIT_EXCEEDED | 429 | 触发限流 | 按 Retry-After 退避重试 |
| SYSTEM_INTERNAL_ERROR | 500 | 服务内部错误 | 提示稍后重试并上报 traceId |
一份能减少群里答疑的接口文档,下面 6 项缺一不可:调用方最常问的「怎么鉴权、参数怎么传、报错怎么办、会不会重复扣款、被限流了怎么办、接口会不会变」,都提前写在文档里。
| 必备字段 | 说明 | 示例 / 要点 |
|---|---|---|
| 鉴权方式与示例 | 认证方案、请求头格式,附一条可直接复制的调用示例 | Authorization: Bearer <token> + 完整 curl 示例;多环境给测试账号 |
| 参数类型与必填 | 每个参数标明位置、类型、必填性与默认值 | query / body / path 分清;page(int, 可选, 默认 1);枚举列出取值 |
| 错误码全表 | 业务码 + HTTP 状态 + 客户端处理建议一表收齐 | 客户端按 code 分支处理,不解析 message 文案;附排查用 traceId 说明 |
| 幂等说明 | 标明哪些接口幂等,非幂等接口如何防重 | POST 支付类注明 Idempotency-Key 生成规则与结果缓存时长(如 24h) |
| 限流阈值 | 接口 / 应用维度的调用频次上限与超限响应 | 如 100 QPS / 应用;超限返回 429 与 Retry-After,写明如何申请提额 |
| 变更记录 | 版本、日期、变更点、是否破坏性变更 | v2.1.0 2026-08-01:新增 status.in 过滤(非破坏);破坏性变更提前公告迁移期 |
重复点击、超时重试、MQ 重投都会造成重复写入。五种主流方案按「防什么、花多少成本」选型;资金类强一致场景建议唯一索引兜底 + Idempotency-Key 组合,其余按业务复杂度单选即可。
| 方案 | 实现要点 | 成本 | 适用场景 |
|---|---|---|---|
| 数据库唯一索引 | 业务唯一键(订单号 / 流水号)建唯一索引,重复插入报错捕获后转查询返回 | 最低,一条 DDL | 插入型业务的最终防线;任何方案都建议保留兜底 |
| 防重 Token | 进页面先领一次性 Token,提交时校验并删除(校验 + 删除必须原子,如 Lua / DEL 返回值判断) | 中,多一次交互 + Redis | 表单防重复提交,前后端配合改造 |
| 状态机流转 | 更新带前置状态条件:UPDATE orders SET status='PAID' WHERE id=? AND status='INIT',影响行数为 0 即重复请求 | 低,SQL 层面天然原子 | 订单 / 工单等状态流转清晰的业务 推荐 |
| 分布式锁 | 以业务键加锁(SETNX / Redisson),锁内查重 - 执行 - 释放;注意锁过期续期与释放校验持有者 | 较高,引入锁服务与超时设计 | 多步复杂操作去重;非幂等逻辑的临时补救 |
| Idempotency-Key 请求头 | 客户端生成唯一键,服务端按 Key 缓存首次结果,重放直接返回;并发到达返回 409 或等待首次完成 | 中,需结果缓存 + 并发控制 | 开放 API / 支付网关标准做法(Stripe 模式) |