后端开发

RESTful API 设计最佳实践:从资源建模到版本演进

2025-05-15 wangjun 13 min read
{}

REST 的核心:资源

REST 把系统抽象成资源(名词),用 HTTP 方法(动词)表达操作。设计得好不好,先看 URL 一眼就能判断。

资源建模规范

# 命名:复数名词 + 小写 + 中划线
GET    /api/users            # 列表
POST   /api/users            # 创建
GET    /api/users/42         # 单个
PATCH  /api/users/42         # 部分更新
DELETE /api/users/42         # 删除

# 子资源:层级关系
GET  /api/users/42/orders    # 用户的订单
GET  /api/users/42/orders/7  # 用户的某个订单

# 动作型操作:用子资源或自定义端点表达
POST /api/orders/7/cancel    # 取消订单(非动词化:/api/cancelOrder ✗)

避免在 URL 里放动词(/getUserData/deleteOrder 都是反模式)。

HTTP 方法语义与状态码

方法    语义        成功响应        场景
GET     读         200/206        查询列表、详情
POST    新建       201 + Location 创建资源
PUT     全量替换   200            完整覆盖
PATCH   部分更新   200            只改个别字段
DELETE  删除       204            幂等删除

# 常见错误码
400 参数错误    401 未认证    403 无权限
404 不存在      409 冲突     429 限流
422 校验失败    500 服务端异常

原则:语义要精确 -- 未登录返回 401 而非 403;资源不存在返回 404 而非 500。

分页:offset 还是 cursor?

# offset 分页(简单,但深翻页慢)
GET /api/articles?page=3&size=20
GET /api/articles?offset=40&limit=20

# cursor 分页(推荐,稳定且快)
GET /api/articles?cursor=eyJpZCI6MTAwfQ&limit=20
# 返回:{ "data": [...], "next_cursor": "..." }

列表数据量大且不断变化时,offset 翻页会出现重复/遗漏,cursor(游标,基于排序字段)才是正解。

统一错误结构

{
  "code": 42201,            // 业务错误码,前端可直接判断
  "message": "手机号格式不正确",
  "field": "phone",         // 字段级错误定位
  "request_id": "a1b2c3"    // 日志追踪
}

错误响应必须结构化,别只丢一行字符串。配合 request_id,线上排障效率翻倍。

版本与幂等

  • 版本:用 URL 前缀 /api/v1/ 或请求头 Accept: application/vnd.api+json;version=1,破坏性变更必须升版本
  • 幂等:POST 创建用 Idempotency-Key 请求头,重试不产生重复订单/支付
  • 字段裁剪:支持 ?fields=id,title?include=author,避免超载响应
REST API 是团队间的"接口契约" -- 命名一致、语义精确、错误可读,就是最好的文档。