写WebAPI,第一件事不是写代码,是想清楚接口给谁用。给前端SPA调,和给第三方开发者调,设计完全两码事。我见过一个团队,内部用的接口返回结构里嵌了五层嵌套,前端每次取值都得写一长串
res.data.result.list[0].items。后来要开放给合作伙伴,对方看了文档直接说"你们先改改再来谈"。RESTful不是圣经。很多人把GET、POST、PUT、DELETE当成铁律,结果遇到批量删除还得发一堆DELETE请求。实际项目中,
POST /api/users/batch-delete比循环调DELETE干净得多。HTTP方法只是约定,不是法律。关键是语义清晰,调用方看一眼URL和参数就知道会发生什么。状态码要用对。我见过接口不管成功失败都返回200,然后在body里塞
{ "code": 500, "msg": "error"}。这种做法让HTTP缓存、监控、网关全部失效。401就是401,404就是404,500就老老实实500。有一次排查线上问题,发现某个服务把所有异常都包成200返回,结果Nginx的access log里全是200,根本看不出错误率。后来改成正确的状态码,监控立刻就能报警了。参数校验别偷懒。前端校验是给用户体验的,后端校验是保命的。有人觉得前端已经限制了输入长度,后端就不查了。结果被人直接curl调接口,塞了几MB的字符串进去,数据库字段直接截断报错。用框架自带的校验器,比如Java的Bean Validation、Python的Pydantic,几行注解的事。别自己写一堆if-else,又丑又容易漏。
版本管理要提前想。
/api/v1/users这种URL路径带版本号最直观。也有人在Header里放Accept: application/vnd.myapp.v2+json,看起来优雅,但调试的时候用curl还得手动加Header,麻烦。路径版本号虽然不够"纯粹",但胜在简单直接。我倾向于URL带版本,等v2真的不兼容v1的时候,老接口还能继续跑着,给调用方留迁移时间。错误信息要具体。"参数错误"这四个字等于没说。好的错误返回长这样:
{ "error": "INVALID_EMAIL", "message": "邮箱格式不正确", "field": "user_email"}。调用方拿到之后能直接定位问题,不用翻文档猜。错误码用字符串比数字好,数字码过一阵子自己都忘了哪个是哪个。字符串码自解释,USER_NOT_FOUND比10023强太多。分页别用offset。数据量小的时候
?page=1&size=20没问题,数据一多,翻到后面几页查询越来越慢,因为数据库要扫描前面所有行。用游标分页,传个last_id或者时间戳,每次查WHERE id > last_id LIMIT 20,速度稳定。前端可能觉得麻烦,但接口响应时间从2秒降到50毫秒的时候,他们就不抱怨了。限流要做。哪怕内部接口也要做。曾经有个定时任务写错了,每秒往接口发几千个请求,直接把服务打挂。后来加了令牌桶限流,单IP每秒最多100次,超了返回429。简单有效。别等出事了再加,那时候已经在救火了。
文档用Swagger或者OpenAPI自动生成。手写文档一定会过期。代码改了文档没改,调用方按文档调不通,然后来问你,你再去翻代码,浪费双方时间。注解写清楚,启动时自动生成文档页面,代码即文档。省事。
最后说一句,接口设计没有绝对的对错。团队内部达成一致,调用方用着舒服,出问题能快速定位,就是好设计。别为了"优雅"把简单事情搞复杂。
顶: 8998踩: 4






评论专区