跳转到内容

Rest Api设计规范

资源命名、HTTP 方法、URI、分页、排序、过滤


[Requirement] 资源使用名词复数形式,全部小写,单词之间用连字符(kebab-case),不使用动词。

[Standard] 资源层级不超过 3 级(/a/{aId}/b/{bId}/c/{cId}),超过时通过过滤参数或独立资源替代。

资源正确错误
用户集合/users/user、/getUsers、/UserList
用户详情/users/{userId}/getUserById
用户订单/users/{userId}/orders/getUserOrders
订单商品/orders/{orderId}/items/orderItems?orderId=...

[Standard] 子资源用于表达”从属”关系,且子资源的主键由父资源限定。

[Default] 多对多或无强从属关系的关联,使用查询参数过滤而非嵌套路径。

# 从属关系:订单属于某个用户
GET /users/123/orders # 获取用户 123 的订单
POST /users/123/orders # 为用户 123 创建订单
# 非从属:标签与文章多对多,用过滤
GET /articles?tag=ai # 获取标签为 ai 的文章
# 反模式 1:动词 + 名词(RPC 风格)
GET /getUsers
POST /createUser
POST /deleteUser?id=123
# 反模式 2:层级过深
GET /companies/1/departments/2/teams/3/members/4 # 4 级,应改为 /members?teamId=3
# 反模式 3:大小写混用或驼峰
GET /userOrders/MyFirstOrder # 应使用 kebab-case:/user-orders/my-first-order

[Requirement] 严格遵守 HTTP 方法语义,禁止用 POST 替代所有操作。

方法语义幂等安全典型用途
GET读取资源是是查询列表/详情
POST创建资源否否新建
PUT全量替换资源是否整体更新
PATCH部分更新资源否*否局部字段更新
DELETE删除资源是否删除

*PATCH 本身不保证幂等,但业务实现应尽量幂等(见幂等性规范)。

[Standard] 成功响应使用恰当的状态码,禁止一律返回 200 OK。

场景状态码说明
GET 成功200返回资源
POST 创建成功201响应 Location 头指向新资源
PUT/PATCH 成功200返回更新后的资源
DELETE 成功204无响应体
异步任务已接受202任务入队,返回任务 ID
参数错误400校验失败
未认证401缺少或无效 Token
无权限403已认证但无权操作
资源不存在404URI 或目标资源不存在
冲突409并发冲突/唯一性冲突
服务端错误500未预期异常
服务不可用503过载或维护中
# 创建用户
POST /v1/users HTTP/1.1
Content-Type: application/json
 
{"name":"张三","email":"zhang@example.com"}
 
HTTP/1.1 201 Created
Location: /v1/users/1001

[Requirement] URI 结构为:/{api-prefix}/{version}/{resource}/{id},其中 api-prefix 统一为 /api。

[Standard] 路径参数使用资源名的 camelCase 形式:/users/{userId}、/orders/{orderId}。

[Default] API 版本放在路径中(见《API版本管理规范》),不使用域名区分版本。

https://api.example.com/api/v1/users/1001/orders?status=paid&page=2&page_size=20
└───── 域名 ─────┘ └prefix┘ └v┘ └资源┘ └id┘ └子资源┘ └──── 查询参数 ────┘

[Standard] 查询参数使用 snake_case(与 Python/Go 后端约定一致),布尔参数显式传 true/false。

参数类型示例
过滤?status=paid&type=vip
时间范围?created_from=2026-01-01T00:00:00Z&created_to=2026-01-31T23:59:59Z
排序?sort=-created_at,price(- 表示降序)
字段选择?fields=id,name,email
分页?page=1&page_size=20 或 ?cursor=eyJ...
# 反模式:操作放入 URI
GET /api/v1/users/123/activate # 应用 PATCH /users/123 {active:true}
POST /api/v1/orders/456/cancel # 应用 POST /orders/456/cancel 或 PATCH
DELETE /api/v1/users/123/avatar # 应用 DELETE /users/123/avatar (子资源 OK)

注:资源上的非 CRUD 动作(如”激活""取消”),当无法用资源状态表达时,可使用动名词子路径:POST /orders/{id}/cancel,但需在文档中明确其幂等性。


[Standard] 列表接口必须支持分页,禁止一次返回全量数据。

策略适用场景参数
偏移分页(Offset)管理后台、可跳页page、page_size
游标分页(Cursor)信息流、大表、实时数据cursor、page_size
seek 分页对性能要求极高的大表last_id、page_size

[Default] 默认偏移分页,参数为 page(从 1 开始)和 page_size(默认 20,最大 100)。

[Requirement] 响应体必须返回分页元信息:total(总数)、page、page_size、total_pages。

{
  "code": 0,
  "message": "ok",
  "data": {
    "items": [ { "id": 1, "name": "张三" } ],
    "pagination": {
      "page": 1,
      "page_size": 20,
      "total": 156,
      "total_pages": 8
    }
  }
}

[Standard] 大表或频繁写入的列表使用游标分页,响应返回 next_cursor,无更多数据时返回 null。

{
  "data": {
    "items": [...],
    "next_cursor": "eyJpZCI6MTAwfQ==",
    "has_more": true
  }
}
# 反模式 1:不分页,返回全量
GET /users # 10 万用户一次性返回
# 反模式 2:page_size 无上限
GET /users?page_size=99999 # 应限制最大 100,超大请求返回 400
# 反模式 3:深分页未限制
GET /users?page=100000&page_size=20 # 大偏移导致慢查询,应改用游标分页

[Standard] 排序参数名为 sort,多字段用逗号分隔,字段前加 - 表示降序,不加表示升序。

[Requirement] 仅允许在文档中声明的可排序字段上排序,未声明字段返回 400。

GET /api/v1/orders?sort=-created_at,total_amount
# 含义:按创建时间降序,同时间按金额升序

[Standard] 简单等值过滤直接用 字段=值;范围过滤使用 字段_from / 字段_to 后缀;模糊匹配使用 字段_like。

操作参数示例说明
等值?status=paid精确匹配
多值?status=paid,pendingIN 查询
范围?amount_from=100&amount_to=500闭区间
模糊?name_like=张LIKE %张%
布尔?active=true显式 true/false

[Requirement] 所有过滤字段必须在白名单内,禁止将原始参数透传到 SQL(防止 SQL 注入)。

[Caution] 模糊查询字段必须有索引,且限制最小长度(如 name_like 不少于 2 个字符)。

# 反模式:未校验字段直接拼 SQL
GET /users?sort=secret_field # 应返回 400 Invalid sort field
GET /users?name_like=% # 空模糊匹配导致全表扫描