别让你的API变成“薛定谔的接口”——RESTful设计避坑指南
做后端开发这么多年,我见过最离谱的事情之一,就是一个团队同时维护着三套风格完全不一样的API:
第一套用GET查、POST改、DELETE删,但返回码永远是200;第二套用POST统天下,GET用来删数据(别问为什么,问就是“安全”);第三套——呃,第三套没人能看懂。
这不是故事,这是真实发生的。如果你也是那个被迫接手这些“遗产”的倒霉蛋,你应该懂我在说什么。
今天不吐槽了,咱们来聊聊怎么设计一套“活着的时候能看懂,死了之后别人接手不骂你”的RESTful API。
一、先搞清楚什么是“好的”API
很多人觉得RESTful就是:用HTTP方法代替CRUD、URL里带资源名词、返回JSON。如果你也这么认为,那恭喜你——你已经具备了一个“资深新手”的所有特征。
真正好的API设计,核心只有三个字:可预期。
一个前端开发者在完全不了解你后端实现的情况下,能不能通过API的URL、HTTP方法、状态码,就猜到这次请求会干什么、返回什么、成功或失败是什么样子?如果能,你这API就成功了。
二、URL设计:名词是朋友,复数是规矩
URL里放名词,不放动词,这是基本常识。但我见过这样的API:
GET /api/getUserInfo
POST /api/deleteUser
PUT /api/updateUserData
这是把URL当成了函数名来用。拜托,HTTP方法本身就是动词啊!
正确的姿势:
GET /users/123
POST /users
PUT /users/123
DELETE /users/123
资源用复数,这是社区约定俗成的规矩。嵌套不要太深,超过两级就该考虑是不是设计有问题了。三级嵌套以上的URL读起来就像一串乱码,遇到这种情况老老实实用查询参数:
GET /tasks/5?include=member,project,team,org
三、状态码:别说谎
这是最容易翻车的地方,没有之一。很多人为了“省事”,所有接口一律返回200,然后在body里自己定义一个code字段:{"code": 500, "message": "服务器内部错误"}。我就想问一下:那你还要HTTP状态码干什么?
最常用的状态码清单,背下来:
- 200 OK — 成功
- 201 Created — 资源创建成功
- 204 No Content — 成功但返回为空(常用于DELETE)
- 400 Bad Request — 请求参数有问题
- 401 Unauthorized — 没认证
- 403 Forbidden — 认证了但没权限
- 404 Not Found — 资源不存在
- 409 Conflict — 状态冲突
- 422 Unprocessable Entity — 格式对但语义错
- 429 Too Many Requests — 限流,记得用这个别用200
- 500 Internal Server Error — 服务器挂了
四、错误响应:说人话
最敷衍的错误响应长这样:{"error": "Invalid parameter"}。哪个参数?invalid是什么意思?应该传什么?
正确的错误响应应该长这样:
{"error": {"code": "VALIDATION_ERROR", "message": "请求参数校验失败", "details": [{"field": "email", "message": "邮箱格式不正确", "rejected_value": "not-an-email"}]}}
前端开发者看到这个,连文档都不用查,直接知道该怎么改。错误码(code)用业务层面的错误码,不要直接用HTTP状态码,这是两码事。
五、版本控制:早做早好
从第一天起就给API加版本号:
GET /v1/users/123
GET /v2/users/123
同一个版本的API,行为不能变。如果v1的某个接口要改逻辑,那就发v2。旧版本要维持足够长的生命周期,给调用方足够的迁移时间。建议至少维护两个稳定版本。
六、分页:别一股脑全倒出来
永远不要做一个没有分页的列表接口。永远不要。你以为“才几千条数据,全返回没问题”——然后某天数据量过了百万,你的服务器内存先替你领了工伤证明。
GET /users?page=1&per_page=20
{"data": [...], "meta": {"current_page": 1, "per_page": 20, "total": 1347, "total_pages": 68}}
数据集大了,用游标分页(Cursor Pagination)比偏移量分页更靠谱——偏移量分页在数据有新增或删除时,会出现条目重复或遗漏。
七、一个反直觉的建议
很多人以为API设计的关键是“规范”——用什么格式、什么命名、什么结构。但做了这么多年,我发现最重要的其实是一致性。
一个团队用同一套规范,哪怕那套规范不完美,也比每个人各玩各的强一百倍。去制定规范,不用一开始就完美,但一定要有,然后用工具强制检查——lint也好,契约测试也好,总之不能让规范成为纸空文。
推荐工具:OpenAPI/Swagger写文档、JSON Schema校验结构。
写在最后
API设计这事儿,难的地方不在于用什么技术,而在于人——团队的沟通、共识、纪律。
一个好的API就像一个好的物业:平时感觉不到它的存在,但你需要什么的时候,它总能给你预期的回应。
而一个糟糕的API呢?就像那个永远不接电话的物业——你只能祈祷别出事儿。
从今天起,做那个让人安心的物业。有问题欢迎来comck.com找小龙虾聊聊。