API · 后端架构 / 接口规范 / 最佳实践

API 设计速查

优秀的 API 设计是后端系统的基石。从资源命名到错误处理,从认证鉴权到版本控制,系统化掌握 RESTful API 设计的最佳实践与原则,构建优雅可维护的 Web API。

10大设计原则 RESTful资源建模 3种认证方案
01

RESTful 规范

REST(Representational State Transfer)是一种架构风格,而非协议。它利用 HTTP 协议的特性来设计 Web API,强调资源导向和统一接口。

资源命名规则

使用名词复数表示资源集合,避免动词。层级关系用斜杠表示,查询参数用于过滤和排序。

// ✅ 推荐 GET /api/v1/users GET /api/v1/users/{id} POST /api/v1/users GET /api/v1/users/{id}/orders GET /api/v1/users?role=admin&page=1 // ❌ 不推荐 GET /api/getUsers POST /api/createUser GET /api/userOrders?userId=123

HTTP 方法语义

方法语义幂等安全请求体响应体
GET获取资源资源表示
POST创建资源创建内容新资源
PUT完整替换完整资源更新后资源
PATCH部分更新可设计差异字段更新后资源
DELETE删除资源空/确认

版本控制策略

推荐将版本号放在 URL 路径中(/api/v1/),便于浏览和调试。也可以通过请求头 Accept: application/vnd.example.v1+json 实现内容协商。

// URL 路径版本(推荐) GET /api/v1/users // Header 版本(内容协商) GET /api/users Accept: application/vnd.myapp.v2+json // 查询参数版本(不推荐) GET /api/users?version=1
02

HTTP 方法语义

正确使用 HTTP 方法是 RESTful API 的核心。每种方法都有明确的语义、幂等性和安全性要求。

GET
获取资源

幂等且安全,不改变服务端状态。可被缓存。查询参数用于过滤、排序、分页。

GET /api/v1/users?page=1&size=20&sort=created_at
POST
创建资源

非幂等,每次调用可能创建不同资源。返回 201 Created 及新资源 Location 头。

POST /api/v1/users { "name": "Alice", "email": "alice@example.com" }
PUT
完整替换

幂等,客户端提供完整的资源表示。缺失字段视为置空或默认值。返回 200 OK。

PUT /api/v1/users/123 { "name": "Alice", "email": "new@example.com" }
PATCH
部分更新

非幂等,只传递需要修改的字段。支持 JSON Patch (RFC 6902) 或 Merge Patch (RFC 7396)。

PATCH /api/v1/users/123 { "email": "new@example.com" }
DELETE
删除资源

幂等,删除后后续 DELETE 返回 404。可返回 204 No Content 或 200 OK。

DELETE /api/v1/users/123
03

状态码使用

选择合适的 HTTP 状态码能让客户端准确理解请求结果。以下是常用的状态码及其使用场景。

2xx · 成功
200 OK        — GET/PUT/PATCH 成功返回资源
201 Created   — POST 创建成功,返回 Location 头
202 Accepted  — 异步任务已接受,稍后处理
204 No Content — DELETE 成功,无返回体
3xx · 重定向
301 Moved Permanently — 资源永久迁移
302 Found              — 临时重定向 ⚠ 部分客户端会把 POST 降级为 GET
304 Not Modified       — 缓存未过期 (ETag/If-None-Match)
307 Temporary Redirect — 临时重定向,保持 HTTP 方法
4xx · 客户端错误
400 Bad Request   — 请求格式错误
401 Unauthorized   — 未认证,需要登录
403 Forbidden      — 已认证但无权限
404 Not Found      — 资源不存在
405 Method Not Allowed — 方法不允许
409 Conflict       — 资源冲突(如重复创建)
422 Unprocessable Entity — 校验失败
429 Too Many Requests    — 请求限流
5xx · 服务端错误
500 Internal Server Error — 服务端内部错误
502 Bad Gateway           — 网关/代理错误
503 Service Unavailable   — 服务暂时不可用
504 Gateway Timeout       — 网关超时
04

错误处理

统一的错误响应格式让客户端能够一致地处理各种错误情况。建议遵循 RFC 7807 Problem Details 规范。

标准错误格式

// RFC 7807 Problem Details { "type": "https://api.example.com/errors/validation", "title": "Validation Error", "status": 422, "detail": "email 字段格式不正确", "instance": "/api/v1/users", "errors": { "email": "请输入有效的邮箱地址", "age": "年龄必须在 1-150 之间" } }

错误码设计原则

  • 分层设计:服务级 + 模块级 + 具体错误码,如 AUTH_TOKEN_EXPIRED
  • 可读性强:使用大写蛇形命名,一目了然
  • 国际化:错误信息支持多语言,通过 Accept-Language 头切换
  • 日志追踪:返回 trace_id 便于服务端排查
  • 安全考虑:不要泄露敏感信息(堆栈、SQL、路径)
06

认证方式

选择合适的认证方式对 API 安全性至关重要。以下是三种主流认证方案的对比和最佳实践。

JWT (JSON Web Token)

无状态认证,token 包含用户信息和 claims。服务端无需存储 session,适合分布式架构。

// Header Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
  • 支持 Token 刷新机制
  • 设置合理的过期时间(Access: 15min, Refresh: 7d)
  • 重要操作需额外验证(OTP/生物识别)

OAuth 2.0

授权框架,允许第三方应用获取有限的资源访问权限。支持授权码、客户端凭证等多种流程。

// 授权码流程 GET /authorize?response_type=code&client_id=xxx&redirect_uri=yyy
  • 授权码 + PKCE 是移动端最佳实践
  • Client Credentials 用于服务间通信
  • 使用 Scope 控制最小权限

API Key

最简单的认证方式,适用于服务端到服务端、公共 API 或内部工具。通过 Header 或查询参数传递。

// Header X-API-Key: sk-xxxxxxxxxxxxxxxx
  • 配合 IP 白名单增强安全性
  • 定期轮换密钥
  • 支持多 Key 隔离不同权限
07

API 版本控制

API 版本管理是保证向后兼容性的关键策略。不同的版本控制方式在可维护性和易用性上各有优劣。

URL 路径版本(推荐)

GET /api/v1/users GET /api/v2/users

优点:直观、易于调试、支持 CDN 缓存、适合大型公共 API。

缺点:URL 不够简洁、版本升级成本高。

Header / 内容协商

GET /api/users Accept: application/vnd.myapp.v1+json

优点:URL 干净、支持细粒度版本控制、适合媒体类型变化。

缺点:调试不便、工具兼容性差、对新手不友好。

版本管理最佳实践

  • 制定明确的版本弃用策略(如:v2 发布后 v1 保持 12 个月)
  • 在响应头中加入弃用警告:Sunset: Thu, 31 Dec 2026 23:59:59 GMT
  • 使用 Deprecation 头提示客户端即将弃用
  • 提供版本迁移指南和 changelog
  • 大版本升级需评估对现有客户端的影响
08

文档与规范

高质量的 API 文档是开发者体验的核心。使用 OpenAPI/Swagger 规范和自动化工具,让文档与代码同步。

OpenAPI / Swagger 规范

// OpenAPI 3.0 示例 openapi: "3.0.3" info: title: 用户管理 API version: "1.0.0" paths: /users: get: summary: 获取用户列表 parameters: - name: page in: query schema: { type: integer, default: 1 } responses: "200": description: 成功返回用户列表 content: application/json: schema: type: array items: { $ref: "#/components/schemas/User" }
📖 Swagger UI

交互式 API 文档浏览,支持在线调试

🔧 Redoc

美观的静态 API 文档渲染

⚡ SpringDoc

Spring Boot 集成 OpenAPI 自动生成

📝 API Changelog

维护清晰的版本变更日志,标注 breaking changes

09

速率限制

Rate Limiting 是保护 API 不被滥用的重要机制。通过标准化的响应头告知客户端当前的限流状态。

标准限流响应头

// 请求响应头 X-RateLimit-Limit: 100 // 窗口内最大请求数 X-RateLimit-Remaining: 85 // 当前窗口剩余请求 X-RateLimit-Reset: 1625097600 // 窗口重置时间戳 // 超限时返回 429 及 Retry-After 429 Too Many Requests Retry-After: 3600

限流算法对比

算法原理优点缺点
令牌桶以固定速率添加令牌,请求消耗令牌允许突发流量,实现简单允许桶容量范围内突发
漏桶请求进入队列,以固定速率处理流量平滑,输出稳定无法应对突发
固定窗口时间窗口内计数,超限拒绝实现最简单窗口边界突发
滑动窗口基于时间区间的精确计数限流均匀,避免边界问题实现较复杂
10

HATEOAS 与 REST 成熟度模型

Richardson Maturity Model(RMM)定义了 REST API 的四个成熟度等级,帮助团队渐进式地提升 API 质量。

Level 0

The Swamp of POX

使用 HTTP 作为传输隧道,一个 URL 一个方法(通常是 POST),所有操作通过请求体区分。常见于早期 SOAP/XML-RPC。

POST /api { "method": "getUser", "params": { "id": 123 } }
Level 1

Resources

引入资源概念,每个资源有独立 URL。但仍使用单一 HTTP 方法。

POST /api/users/123/getOrders POST /api/users/123/createOrder
Level 2

HTTP Verbs

正确使用 HTTP 方法语义(GET/POST/PUT/DELETE),结合状态码表达结果。这是大多数 API 的目标级别。

GET /api/users/123 POST /api/users PUT /api/users/123 DELETE /api/users/123
Level 3

HATEOAS

响应中包含链接(links),客户端通过链接发现可执行的操作,实现超媒体驱动。

{ "id": 123, "name": "Alice", "_links": { "self": "/api/users/123", "orders": "/api/users/123/orders", "update": { "href": "/api/users/123", "method": "PUT" } } }

HATEOAS 设计要点

  • 使用 _linkslinks 字段包含相关资源链接
  • 每个链接包含 href 和可选的 method/rel 属性
  • 分页响应中包含 first/prev/next/last 链接
  • 链接随资源状态变化(如:订单已支付后不再显示"取消"链接)
  • 实现难度高,推荐在 Level 2 的基础上按需采用

接口从能用到好用,还差这些规范

统一响应

{
  "code": "USER_NOT_FOUND",
  "message": "用户不存在",
  "traceId": "8f12a...",
  "data": null
}

分页过滤

GET /api/orders?cursor=xxx&limit=20&sort=-createdAt&fields=id,status,total

幂等请求

POST /api/payments
Idempotency-Key: order_1001_pay_1

废弃策略

Deprecation: true
Sunset: Wed, 30 Dec 2026 23:59:59 GMT

错误码设计

  • HTTP 状态码表达协议层结果。
  • 业务 code 表达可识别错误。
  • traceId 贯穿日志和链路追踪。

API 安全

  • 鉴权、签名、限流、审计不可缺。
  • 敏感字段默认不返回。
  • 管理端接口必须二次校验权限。

OpenAPI 流程

  • 接口先写 schema。
  • CI 校验文档和实现一致。
  • 变更记录写入 changelog。

把规范写进接口契约

Rate Limit Header

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Retry-After: 60

Cursor Pagination

{
  "items": [],
  "nextCursor": "eyJpZCI6MTAwMX0=",
  "hasMore": true
}

OpenAPI 错误响应

components:
  schemas:
    ErrorResponse:
      required: [code, message, traceId]

🔢 版本策略与破坏性变更治理

版本策略示例优点缺点适用场景
URL 路径版本/api/v1/users直观、可缓存、调试方便URL 冗余、版本粒度粗公共 API、多客户端并存
Header 内容协商Accept: application/vnd.myapp.v2+jsonURL 干净、可细粒度演进调试不便、工具兼容差媒体类型频繁演进的中大型 API
查询参数版本/api/users?version=2实现最简单易被缓存误处理、语义弱仅限内部系统快速过渡

破坏性变更清单

  • 删除或重命名响应字段、接口路径。
  • 修改字段类型或必填/选填语义。
  • 新增必填请求参数或请求头。
  • 收紧校验规则、改变错误码含义。

灰度策略

  • 新旧版本并行部署,按流量比例灰度。
  • 按客户端版本或白名单用户切流。
  • 网关层支持双版本路由与快速回滚。
  • 观察错误率与核心指标后再放量。

废弃流程

  • 标记:响应加 Deprecation 与 Sunset 头。
  • 通知:公告与文档标明迁移路径和期限。
  • 观察:统计旧版本调用量与调用方清单。
  • 下线:到期返回 410 Gone,保留排障通道。

📄 分页·过滤·排序约定

page/size 约定

// 管理后台类列表 GET /api/v1/orders?page=1&size=20 { "data": [...], "pagination": { "page": 1, "size": 20, "total": 156, "totalPages": 8 } }

仅用于数据量可控的后台列表;需要展示总条数与跳页时使用。

cursor 约定(默认推荐)

// App 与开放 API,游标保持不透明 GET /api/v1/orders?cursor=xxx&limit=20 { "items": [...], "nextCursor": "yyy", "hasMore": true }

基于索引定位,性能稳定;不暴露内部 ID 与总条数。

过滤命名

  • 精确过滤:?status=active
  • 范围过滤:?createdAt.gte=xxx&createdAt.lte=xxx
  • 多值过滤:?status.in=a,b
  • 模糊搜索用 ?q= 单独承载,不与字段过滤混用。

排序命名

  • 统一 ?sort= 单参数,逗号分隔多字段。
  • 前缀 - 表示倒序:?sort=-createdAt,id
  • 字段名与资源属性命名风格全站统一。
  • 服务端白名单校验可排序字段,防止全表扫描。

深分页提醒

  • 限制最大页码与最大 size,拒绝超深偏移请求。
  • 导出与同步场景改用 cursor 流式拉取。
  • 列表接口强制默认分页,不提供无界全量查询。

🚦 错误码分层与幂等键设计

错误码分层结构

// HTTP 状态码表达协议层,业务码表达业务层 HTTP/1.1 422 Unprocessable Entity { "code": "ORDER_PAY_DUPLICATE", "message": "订单已支付,请勿重复提交", "traceId": "8f12a3...", "data": null }

HTTP 表达成败大类;业务 code 供客户端分支处理;message 面向用户展示;traceId 串联服务端日志。

幂等键设计(Idempotency-Key)

// 客户端生成唯一键,服务端幂等处理 POST /api/v1/payments Idempotency-Key: 6b1f2c9e-xxxx // 首次请求:执行并缓存结果 // 重放请求:直接返回首次结果 // 并发请求:返回 409 或等待首次完成
  • POST 与支付类接口强制要求幂等键
  • 幂等结果按 Key 缓存并设置 TTL(如 24h)
  • Key 与用户或租户维度绑定,防跨账户碰撞
  • 响应回显 Idempotency-Key 便于排查

全局错误码表(节选示例)

业务码HTTP含义客户端处理建议
AUTH_TOKEN_EXPIRED401访问令牌过期静默刷新 Token 后重试
PARAM_INVALID422参数校验失败按 errors 字段定位表单项
ORDER_NOT_FOUND404订单不存在引导刷新列表
ORDER_PAY_DUPLICATE409重复支付查询支付结果后收敛页面
RATE_LIMIT_EXCEEDED429触发限流按 Retry-After 退避重试
SYSTEM_INTERNAL_ERROR500服务内部错误提示稍后重试并上报 traceId
幂等键方案
幂等键方案:同一 Key 二次请求直接返回上次结果,写操作不重复执行

🧾 接口文档必备字段

一份能减少群里答疑的接口文档,下面 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 模式)