写一个能用的 API 很简单,写一个让调用方觉得顺手的 API 却是一门学问。一个设计良好的 RESTful API 能让前后端协作效率翻倍,而糟糕的 API 则让团队陷入无尽的沟通噩梦。

URI 命名原则

资源用名词复数,不用动词:

❌ 不好 ✅ 好
/getUsers /users
/createOrder /orders
/deleteUser?id=1 /users/1

HTTP 方法本身就表达了动作——GET(获取)、POST(创建)、PUT/PATCH(更新)、DELETE(删除)。不要在 URI 上再重复一遍。

我的踩坑记:404 用成了业务错误

维护一个老项目时,前辈把”用户不存在”返回 404,把”用户已禁用”也返回 404,把”权限不足”还是返回 404。前端为了区分这三种情况,不得不去解析 response.body 里的 error.code 字段——HTTP 状态码形同虚设。

重构时我把语义重新梳理:”用户不存在”是真 404(资源确实不在)、”用户已禁用”改成 422(业务逻辑不通过)、”权限不足”改成 403。前端立刻清爽了一半,错误处理分支从一坨 if-else 变成了按状态码分流。HTTP 状态码不是装饰,它是 API 契约的一部分——乱用会让调用方多写 3 倍代码。

HTTP 方法语义

方法 语义 幂等 示例
GET 获取资源 GET /users/123
POST 创建资源 POST /users
PUT 全量替换 PUT /users/123
PATCH 部分更新 PATCH /users/123
DELETE 删除资源 DELETE /users/123

状态码的正确用法

状态码 含义 使用场景
200 OK 成功 GET、PUT、PATCH
201 Created 已创建 POST
204 No Content 成功无返回 DELETE
400 Bad Request 格式错误 参数校验失败
401 Unauthorized 未认证 Token 缺失
403 Forbidden 无权限 权限不足
404 Not Found 不存在 记录不存在
409 Conflict 冲突 重复创建
422 Unprocessable 语义错误 业务逻辑不通过
500 Internal Error 服务器错误 未预期异常

常见误区: 把所有错误都返回 400。应该区分请求格式错误(400)、认证失败(401)、业务逻辑错误(422)和服务器错误(500)。

错误响应体标准

1
2
3
4
5
6
7
8
9
{
"error": {
"code": "VALIDATION_ERROR",
"message": "用户名已存在",
"details": [
{ "field": "username", "reason": "duplicate" }
]
}
}

统一错误格式让调用方用一个分支就能处理所有异常——远比每个接口返回不同的错误结构要友好。

分页与过滤标准

1
GET /users?page=2&limit=20&sort=-created_at&q=search&status=active

返回体包含分页元数据:

1
2
3
4
5
6
7
8
9
{
"data": [ /* ... */ ],
"meta": {
"page": 2,
"limit": 20,
"total": 156,
"totalPages": 8
}
}

API 版本管理

三种主流方案:

  1. URI 版本(最直观):/v1/users/v2/users
  2. 请求头版本Accept: application/vnd.myapp.v2+json
  3. 查询参数版本/users?version=2

对于公开 API,URI 版本最简单粗暴、调用方最容易理解。

总结

好的 API 设计不依赖天才的直觉,而是依赖一套清晰的规范。URI 命名有规律、状态码语义准确、错误格式统一、分页过滤规范——这些看似基础的东西积累起来,就是团队协作的超级润滑剂。