为什么选了 RESTful?
项目初期,团队决定用 RESTful 设计 API。理由很充分:标准、简单、易理解。大家都学过,上手快。
刚开始确实不错,资源用名词,操作用 HTTP 方法,看起来很清晰。但用久了,问题就来了。
第一个坑:复杂查询
RESTful 的查询很简单,但我们的业务查询很复杂。比如要查询"最近 30 天注册的用户,按活跃度排序,只返回前 10 个"。
用 RESTful 的话,URL 会变成这样:
GET /users?registered_after=2025-03-05&sort_by=activity&limit=10参数多了,URL 就变得很长。而且有些查询条件很复杂,用 query string 根本表达不了。
我们后来用了 GraphQL,复杂查询用 GraphQL,简单查询还是用 RESTful。
第二个坑:批量操作
RESTful 的批量操作很麻烦。比如要删除多个用户,RESTful 的做法是:
- 发多个 DELETE 请求:慢,而且不是原子操作
- 用 POST /users/batch-delete:不符合 RESTful 规范
- 用 PATCH /users:语义不清晰
我们最后用了 POST /users/batch,虽然不符合 RESTful,但至少能用。
第三个坑:动作型操作
有些操作不是 CRUD,而是动作。比如"发送邮件"、"导出报表"、"重置密码"。
用 RESTful 的话,要这样设计:
- POST /emails:发送邮件
- POST /reports/export:导出报表
- POST /users/:id/reset-password:重置密码
虽然能用,但总觉得别扭。这些操作不是创建资源,而是执行动作。
我们后来用了 RPC 风格的 API,动作型操作用 POST /rpc/send-email 这样的格式。
第四个坑:版本管理
RESTful 的版本管理是个问题。URL 里加版本号(/v1/users)还是 Header 里加版本号(Accept: application/vnd.api+json;version=1)?
我们选了 URL 版本号,因为简单直接。但问题来了,版本多了,URL 就乱了。
而且有些 API 要同时支持多个版本,代码就变得很复杂。
第五个坑:错误处理
RESTful 的错误处理用 HTTP 状态码,但业务错误怎么表示?
我们试过几个方案:
- 用 HTTP 状态码:但业务错误很多,状态码不够用
- 用错误码:在响应体里加 error_code,但每个客户端都要解析
- 用错误对象:统一错误格式,但实现复杂
最后统一了错误格式,所有错误都返回 200,错误信息在响应体里。虽然不符合 RESTful,但至少统一。
我们的解决方案
用了一年多 RESTful,我们发现:
- CRUD 操作用 RESTful,简单清晰
- 复杂查询用 GraphQL,灵活强大
- 动作型操作用 RPC 风格,语义明确
- 批量操作用自定义端点,实用优先
不是所有 API 都要 RESTful,看场景选方案。
API 设计的经验
做了几年 API 设计,总结几个经验:
1. 一致性比规范重要
整个 API 要保持一致的风格,比严格遵循某个规范更重要。如果有些 API 不符合 RESTful,但风格一致,也能接受。
2. 实用优先
API 是给人用的,不是给规范用的。如果 RESTful 不适合,就用其他方案。实用优先,规范其次。
3. 文档要清晰
API 文档要清晰,参数、返回值、错误码都要说明。我们用了 OpenAPI(Swagger),自动生成文档,方便很多。
4. 版本管理要早规划
API 版本管理要早规划,不然后面改起来很麻烦。我们一开始没规划,后来改 API 就很痛苦。
如果重新开始
如果重新设计 API,我会:
- CRUD 用 RESTful,简单清晰
- 复杂查询用 GraphQL 或自定义查询语言
- 动作型操作用 RPC 风格
- 统一错误格式和版本管理
- 做好文档和测试
总结
RESTful 不是银弹,不是所有 API 都适合 RESTful。看场景选方案,实用优先。
如果你们的 API 设计也有问题,建议先看业务场景,再选技术方案。不要为了 RESTful 而 RESTful,实用最重要。
另外,API 设计要早规划,不然后面改起来很麻烦。我们吃过这个亏,后来花了很多时间才理清楚。
评论区