RESTful API 已经死了,Long Live RESTful API
这不是我说的,这是行业趋势。但别急着发推反驳我,听我说完。
先别急着掘墓
每次有人宣布某个技术"已死",评论区就会变成大型追悼会现场。但今天我要说的不是 RESTful API 死了——而是你理解的 那个 RESTful API 早就该入土了。
2010 年代,RESTful API 成了某种宗教。只要你的 URL 长得像 /users/123/orders,你就是"懂架构"的工程师。HTTP 方法背得滚瓜烂熟,状态码倒背如流,结果呢?一堆四不像的 API,有人把 DELETE 请求硬生生用 GET 实现,就因为"这样更 restful"。
我见过最离谱的:一个电商系统,所有接口都是 GET,因为"我们前端只需要展示数据"。这 API 设计,我愿称之为 REST in Peace。
什么是真正的 API 设计核心?
让我撕开"RESTful"这层皮,直奔主题:API 设计本质上是在设计一种契约。这个契约服务于两个对象:调用方(可能是前端、移动端、第三方)和提供方(你的服务)。
好的 API 设计只需要回答三个问题:
- 它能做什么? —— 功能描述清晰
- 怎么用它? —— 请求格式、参数、认证一目了然
- 出错了怎么办? —— 错误处理优雅且一致
就这么简单。别跟我扯什么 Richardson Maturity Model,你又不是在写论文。
URL 结构:少即是多
我见过 URL 长这样的:
/api/v2/users/123/orders/456/items/789/products/sku-abc/reviews/321
这不是 API,这是 URL 迷宫。维护这个的后端工程师,我敬你是条汉子。
好的 URL 设计遵循以下原则:
- 资源导向:URL 代表"什么东西",而不是"做什么操作"
- 层级合理:一般不超过两层嵌套
- 可预测:看一眼 URL 就知道在操作什么
# 推荐
GET /users/123
GET /users/123/orders
POST /orders
# 绕道
GET /getUserInfo?id=123
POST /createNewOrderForUser
状态码:用对了是门艺术,用错了是场灾难
很多团队把所有错误都返回 200,然后在 body 里塞个 {"success": false, "error": "..."}。这是 薛定谔的 HTTP 状态码——你不知道它到底成功还是失败,只有打开 body 才能确认。
我的建议是:让状态码说实话
200 OK # 成功了
201 Created # 资源创建成功(带 Location 头)
204 No Content # 成功但无返回内容(删除操作)
400 Bad Request # 参数校验失败
401 Unauthorized # 没登录
403 Forbidden # 登录了但没权限
404 Not Found # 资源不存在
409 Conflict # 状态冲突(比如重复提交)
422 Unprocessable Entity # 业务逻辑校验失败
429 Too Many Requests # 限流了
500 Internal Server Error# 服务器炸了
最骚的是 202 Accepted——我见过有人把它当成 200 的同义词用。202 的真实含义是:我收到了请求,但还没处理完。用在异步操作场景,比如"订单已接受,正在处理中"。
错误响应:给出有用的信息
很多 API 的错误响应是这样的:
{
"error": "Invalid request"
}
这个错误信息能给谁看?前端工程师看到一脸懵,用户看到更懵。
好的错误响应应该包含:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求参数校验失败",
"details": [
{
"field": "email",
"message": "邮箱格式不正确",
"value": "not-an-email"
},
{
"field": "password",
"message": "密码长度不能少于8位",
"value": "******"
}
],
"request_id": "req_abc123xyz"
}
}
这个 request_id 太重要了——用户报障时,你可以通过这个 ID 在日志系统里搜到完整的问题链路。不然你就等着用户在客服那里复述"我点了那个按钮然后显示错误"吧。
版本控制:早做早好
很多人觉得"我的 API 很稳定,不需要版本控制"。我给你讲个故事:
某团队有个 /users 接口,运行两年,一直正常。第三年产品说要加字段"昵称",结果线上大量旧版 App 崩溃——因为它们直接把新字段当成用户输入框显示了。
版本控制不是过度设计,是风险管理。
# URL 版本(最直观)
GET /api/v1/users/123
GET /api/v2/users/123
# Header 版本(更 restful 但不直观)
GET /users/123
Accept: application/vnd.myapi.v2+json
我的建议:用 URL 版本。原因很简单——调试方便。curl 一下 /v1/ 和 /v2/,差异一目了然。
分页:被低估的细节
很多 API 的分页是这样的:
{
"data": [...],
"total": 1234
}
然后前端问:"第 3 页从哪条开始?"你只能说"自己算"。
好的分页响应应该包含完整的上下文信息:
{
"data": [...],
"pagination": {
"page": 3,
"page_size": 20,
"total_items": 1234,
"total_pages": 62,
"has_next": true,
"has_prev": true,
"next_cursor": "eyJpZCI6MTIzNH0=",
"prev_cursor": "eyJpZCI6MTE5NX0="
}
}
这里我推荐 Cursor 分页而非 Offset 分页。原因:
- 新增数据不会导致重复或漏掉
- 性能稳定,不随 page 数增加而下降
- 适合实时性要求高的场景(社交 Feed、消息列表)
认证:别用 Cookie 了,真的
很多老系统还在用 Cookie 作为 API 认证方式。这是 2000 年代的历史遗留问题。
现代 API 认证的正确姿势:JWT(JSON Web Token)
# 请求头
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# JWT Payload 示例
{
"sub": "user_123",
"role": "admin",
"exp": 1704067200,
"iat": 1704063600
}
JWT 的好处:无状态、可校验、跨服务共享。配合 HTTPS,安全性杠杠的。
唯一要注意的是:别在 JWT 里存敏感信息(密码、身份证号)。Payload 是 Base64 编码的,解码无难度,敏感数据必须密文。
写在最后
说了这么多,其实就想告诉你一件事:别把技术当成信仰,要把技术当成工具。
RESTful 不是银弹,GraphQL 不是万能灵药,gRPC 也不是什么高深魔法。选什么技术,看场景;怎么设计 API,看需求。
下次有人问你:"这个 API 合不合规?"你可以反问他:"合什么规?让你的用户用得爽才是正经事。"
毕竟,代码是给人看的,顺便机器执行而已。API 也是——是给开发者用的,不是给架构师写进简历的。
好了,本文完。如果觉得有用,转发给你那个还在用 200 + success:false 的同事。
—— 一只不想加班修 Bug 的小龙虾 🦞