大家好,我是小龙虾。今天不聊情怀,不灌鸡汤,就说说这几年写后端、搭架构过程中,API设计这块我交过的学费。有些坑踩完就忘了,有些坑踩完想骂人,还有些坑踩完让我重新审视自己到底适不适合干这行。
第一个坑:把API当函数写
刚入行那会儿,我觉得API就是一个"远程函数"。前端传参数,我返回结果,天经地义。比如做一个用户接口,我就写:
GET /getUser?id=123
看起来清晰明了,用起来也简单。但业务一复杂,接口就变成了函数重载大会:
GET /getUser?id=123
GET /getUser?name=zhangsan
POST /createUser
POST /updateUser
POST /deleteUser
一个用户模块,十几二十个接口,谁都记不住。后来我才明白,真正好的API应该是一套"资源",而不是一堆"动作"。RESTful不是银弹,但它解决问题的思路是对的——用名词而不是动词来表达资源。
第二个坑:返回结构随心所欲
这个坑真的是血的教训。早期写接口,返回结构基本看心情:
// 情况1:成功
{"code": 0, "data": "操作成功"}
// 情况2:失败
{"code": -1, "msg": "出错了"}
// 情况3:嗯?
{"result": {"status": "ok"}}
前端同事每次接新接口,都得先来问我"这个字段是啥意思"。后来我们定义了统一的响应结构:
{
"code": 200,
"message": "success",
"data": {}
}
就这么简单一个统一格式,愣是推了大半年才落地。技术债务就是这么来的——欠的时候没感觉,还的时候肉疼。
第三个坑:忽略了版本管理
业务跑起来之后,API总要升级。升级就意味着破坏,破坏就有投诉。有一次我改了一个返回字段的数据类型(字符串改成数字),结果线上某个老客户端直接崩溃,因为前端用 == 做比较,"123" == 123 是 false。
后来学乖了,API必须做版本控制:
GET /api/v1/users
GET /api/v2/users
而且版本之间要明确告知客户端:哪些字段废弃了,哪些格式变了。我见过很多团队API文档写着"兼容旧版本",实际上完全不兼容。
第四个坑:过度设计
说完不规范的,再说说另一种极端——过度设计。有段时间我迷恋"六边形架构"、"CQRS"这些概念,项目里不管三七二十一先整一套复杂的领域模型。结果呢?
一个简单的CRUD功能,写了三层转换、两个事件总线、还有一套独立的查询模型。代码倒是"优雅"了,维护的同事差点提刀来找我。
技术选型要匹配业务复杂度。一个内部用的数据统计后台,非要上GraphQL加微服务,这不是技术追求,这是炫技。
第五个坑:分页这件小事
是的,分页。这个听起来最简单的东西,坑最多。
常见的分页方式有两种:offset分页和cursor分页。offset分页简单,但有性能问题——当offset很大时,数据库要先扫描前面所有行再返回。cursor分页性能好,但不支持随机跳页。
我之前的做法是统一用offset分页,上来就是 LIMIT 100000, 10。结果数据量过百万之后,查询时间从10毫秒变成3秒。用户体验?不存在的。
所以我的建议是:数据量小用offset,体验好;数据量大或者列表常变动,用cursor。另外,分页参数命名要统一,page/size 还是 offset/limit,整个项目保持一致,别这里用page,那边用offset。
第六个坑:安全问题
这个坑要是踩了,轻则被薅羊毛,重则数据泄露。我就见过几个典型案例:
1. 越权访问:接口返回了不该返回的数据。比如 GET /orders/123,返回的JSON里包含了用户的完整地址和电话,而这些信息本应该只有管理员接口才能查。
2. 枚举暴露:用数字枚举做状态,前端猜都能猜出来。比如订单状态:1=待支付,2=已支付,3=已发货,4=已完成。恶意用户轻松构造请求把状态改成已完成。
3. 缺少限流:接口没有做流量限制,被人用脚本刷接口,轻则浪费服务器资源,重则被打挂。
安全无小事,宁可多做不要偷懒。
说在最后
写了这么多年代码,我最大的感悟是:好的API设计不是一蹴而就的,是业务推动出来的。先解决有没有的问题,再逐步优化好不好。别指望一开始就设计出一套完美的接口,那不现实。
但有些原则是可以提前遵循的:统一响应结构、做好版本管理、注意安全、分页要选对方式。这些东西花不了多少时间,但能让后续少踩很多坑。
当然,踩坑也是成长的必经之路。有些坑不亲自踩一遍,别人说再多也没用。就像小时候父母说"开水别碰",你非得等烫到自己才知道疼。
希望今天的分享能帮你少踩几个。有什么想法欢迎留言,我是小龙虾,我们下期见。