写API这事儿,我见过太多「技术债」现场了
作为一个写过无数接口的老油条,今天必须来聊聊API设计里那些让人头秃的烂设计。不是为了喷而喷,是真的见过太多项目被糟糕的API设计拖垮到重构的。看完这篇,希望你的下一个接口不要成为别人的噩梦。
一、RESTful?不是你想怎么用就怎么用的
现在出去面试,开口就是「我们的API是RESTful的」。好,RESTful很好,但不是你说RESTful就RESTful的。很多人对RESTful的理解就是:URL里放名词,HTTP方法用GET/POST/PUT/DELETE,然后自称精通RESTful了。
大哥,你连HATEOAS都没听说过,Hypermedia都不支持,算什么RESTful?
最常见的问题是什么?用一个GET方法干所有事情。查列表用GET,查详情用GET,删数据用GET(传个delete参数),甚至改数据也用GET。你问为什么?他说「GET更安全,浏览器直接能打开」。每次听到这种回答我都想原地升天。
真实案例:某项目查用户详情是 GET /api/getUser?id=123,问他为什么不用 GET /api/users/123,他说「怕用户记不住」。兄弟,你是怕你自己记不住RESTful怎么写吧?
HTTP语义不是摆设。GET就是幂等的、只读的;POST是创建资源的;PUT是更新完整资源的;PATCH是部分更新的。把这些用对了,至少你的API不会让人看了一脸问号。
二、命名:说好的人能看懂呢?
API命名这事儿吧,说难听点,有些接口命名纯属程序员自嗨。团队内部可能大家都知道啥意思,但凡有个新人进来,或者对接别的团队,那个命名就是灾难。
举几个我见过的奇葩命名:
- 拼音+英文混搭型:/api/getUserList、/api/getUserInfo、/api/getUserDetail — info和detail有什么区别?list和detail又有什么区别?
- 缩写狂魔型:/api/qryUsr、/api/updUsrInfo、/api/delUsr — 你是怕别人看懂你在干什么吗?
- 动词堆积型:/api/getUserDataById、/api/getUserDataByName、/api/getUserDataByPhone — 能不能统一一下?
好的命名应该是什么样的?
- 名词复数形式:/users、/orders、/products
- 层级结构清晰:/users/{userId}/orders(用户的所有订单)
- 不用拼音,用英文单词
✅ 正确示范:GET /users/123/orders — 获取ID为123的用户的所有订单❌ 错误示范:GET /api/getUserOrderList?userId=123
三、HTTP状态码:200真的不是万能的
这个问题我见过无数次了:接口报错了,返回200,业务状态码写个-1或者「error」。拜托,HTTP状态码就是用来处理异常情况的,你非要用200包着错误信息返回,让客户端判断到底是成功还是失败,这不是脱了裤子放屁吗?
常见的状态码混乱:
- 资源不存在:返回200 + {code: 404, message: "not found"} — 你是在玩大家来找茬吗?
- 参数校验失败:返回200 + {code: 400, message: "参数错误"} — 前端看到这个200还以为成功了
- 服务器崩了:返回500 + 空响应体 — 客户端解析JSON直接报错
正确的做法是什么?
✅ 成功GET /users/123200 OK{ "id": 123, "name": "张三", "email": "zhangsan@example.com" }✅ 资源不存在GET /users/999404 Not Found{ "error": "用户不存在", "code": "USER_NOT_FOUND" }✅ 参数校验失败POST /users400 Bad Request{ "error": "邮箱格式不正确", "code": "INVALID_EMAIL" }✅ 服务器错误GET /users500 Internal Server Error{ "error": "服务暂不可用", "code": "SERVICE_UNAVAILABLE" }
四、版本管理:没版本号的API都是在耍流氓
很多人觉得「我的API就这么一套,不用版本管理,多干净」。好,你想得美。等你哪天要加字段、改逻辑、删参数的时候,你就知道疼了。老客户端不兼容,新客户端用不了,线上事故就来了。
版本号放哪儿?常见的有三种:
- URL路径:/api/v1/users — 最直观,最常见
- Query参数:/api/users?version=1 — 不推荐,容易被忽略
- Header:Accept: application/vnd.api+json;version=1 — 规范但不够直观
我个人推荐URL路径的方式,简单粗暴,一目了然。访问/v1/users就知道是老版本,/v2/users就是新版本。
冷知识:GitHub的API就是URL路径版本管理。访问 api.github.com/v3/users 是老版本,api.github.com/users 是新版本(其实v3存在了很多年)。这才是正经大厂的做法。
五、返回结构:标准化是基本礼仪
很多项目的接口返回结构是这样的:
// 接口A{ "code": 0, "msg": "成功", "data": { ... } }// 接口B{ "status": "ok", "result": { ... } }// 接口C{ "success": true, "info": { ... } }// 接口D{ ... } // 直接返回数据,没任何包裹
五个接口五种返回格式,前端每次对接新接口都要写一堆特殊处理代码。这就是没有统一响应结构的后果。
强烈建议所有接口统一响应结构:
{ "code": 0, // 业务状态码,0表示成功 "message": "success", // 描述信息 "data": { ... } // 业务数据}
分页列表也要统一:
{ "code": 0, "message": "success", "data": { "list": [ ... ], "pagination": { "page": 1, "pageSize": 20, "total": 100, "totalPages": 5 } }}
统一了格式,前端封装一个axios拦截器,所有的错误处理、loading状态、异常提示都可以统一处理。它不香吗?
六、安全:你的API被陌生人访问过吗?
这个话题很多人觉得自己做得很好,其实漏洞一堆。我来说几个常见的:
1. 没有参数校验
前端说「我做了校验」,然后后端直接存进数据库。结果SQL注入、XSS攻击了解一下?前后端校验都要做,后端不能信任任何前端传来的数据,这是基本常识。
2. 越权访问
// 危险示例GET /api/orders/456// 返回了这个订单的完整信息,包括其他用户的订单// 正确做法:后端要校验当前登录用户是否有权限访问这个订单if (order.userId !== currentUser.id) { return 403 Forbidden; }
3. 敏感信息明文传输或明文存储
密码加密了吗?身份证号加密了吗?手机号加密了吗?日志里打印了完整token了吗?这些细节不注意,出了事就不是技术问题了,是法律问题。
七、写到最后
API设计这事儿,说难听点就是「前人挖坑,后人填坑」。你在设计的时候多花10分钟,后人可能就少掉1小时头发。
几个核心原则记住:
- 语义清晰:HTTP方法用对,状态码用对
- 命名规范:统一、可读、不歧义
- 结构统一:响应格式标准化
- 版本意识:预留扩展能力
- 安全第一:校验、鉴权、加密一个都不能少
好的API是沉默的——调用者不需要看文档就能猜到怎么用,错误信息清晰到能直接定位问题,返回格式统一到可以一键封装。这才是后端该追求的境界。
少写点「能用就行」的代码,多想想未来维护你代码的人会不会半夜爬起来骂你。代码千万行,API第一关。祝大家的接口都能平平安安上线,稳稳当当运行。