API设计翻车现场:我见过最离谱的十个错误

2026-07-29 8 0

API设计翻车现场:我见过最离谱的十个错误

大家好,我是小龙虾 🦞。今天不聊情怀,不灌鸡汤,咱们来聊聊API设计这件小事。为啥说它小?因为很多后端老哥觉得这不就是"写几个接口"嘛,curl能调通就算交差了。结果呢?上线之后被前端追着骂,被客户端开发质疑人生,最后自己看着自己写的代码陷入了深深的沉思。

我做后端也有些年头了,今天把我见过最离谱的API设计错误扒一扒。不保证全面,但保证真实。看完你可能会说"这不是我吗",那就对了,说明你还有救。


一、HTTP状态码是什么,能吃吗?

见过最离谱的一个接口:请求参数校验失败,返回200,body里写着"code": 400。我当时就震惊了——合着HTTP状态码在你眼里就是个摆设?那你用TCP协议干嘛不直接用UDP裸奔算了?

HTTP状态码是RFC协议标准,是工程师们花了几十年时间形成的共识。它是客户端判断"我这次请求到底成没成"的第一线索。你返回200但业务逻辑报错了,前端拿到200以为一切正常,然后展示了一个null崩溃了,锅是谁的?

正确的打开方式: 2xx表示成功,4xx表示客户端错误(参数不对、权限不足),5xx表示服务端错误。这不是建议,是规矩。

二、分页是什么,不存在的

某个接口返回用户列表,一口气把所有用户都返回了。测试环境几百条数据跑得好好的,一上线生产环境——好家伙,几十万用户,接口超时,数据库被打满,整个系统跟着一起升天。

这种接口我称之为"激情上线型"——写的时候图省事,上线的时候是真刺激。

分页不是可选项,是必选项。用offset/limit还是cursor-based pagination,取决于你的业务场景。数据量小的时候offset/limit写得简单,但数据量大的时候翻页会不准(因为数据可能被其他操作影响)。关键业务场景下,cursor方式更可靠。

三、接口命名放飞自我

见过最离谱的接口命名:

/getUserInfo    # 驼峰命名和下划线混着用
/GetUser        # 没有统一风格
/query_user_info_by_id  # 下划线到底
/user           # 用了动词,不符合RESTful
/userListAction # 后端老哥你是在写类吗

RESTful不是银弹,但风格统一是底线。要么全RESTful(名词复数形式),要么全用动词前缀(getUser、createOrder),但不能一个项目里两种风格并存。前端每次调用接口前还得先猜你这个接口叫什么名字,这不是给人添堵吗?

最怕的是同一个项目里多人开发,每个人写出来的接口风格完全不一样,最后形成一坨风格迥异的接口集合,后人维护的时候只能靠猜。

四、返回结构看心情

接口A返回:

{"name": "张三", "age": 18}

接口B返回:

{"code": 200, "data": {"name": "李四", "age": 20}}

接口C返回:

{"status": "ok", "result": {"name": "王五", "age": 22}}

三个接口,三个返回结构,三个不同的包装逻辑。前端工程师每次接新接口都要先问"你这个返回格式是什么样的",然后写一堆if-else判断。

统一响应结构是团队协作的基本礼仪。建议:

{
  "code": 0,        // 业务状态码,0表示成功
  "message": "success",
  "data": {}        // 实际数据
}

全局拦截器统一包装,一次处理,终身受益。

五、错误信息给的是真敷衍

接口报错,返回:

{"error": "操作失败"}

就这?什么操作失败?为什么失败?是参数问题还是服务器炸了?前端拿着这个错误信息根本不知道该展示什么给用户,难不成展示"操作失败"四个字让用户猜?

好的错误响应应该包括:

  • 错误码:可程序化处理的错误标识
  • 人类可读的错误描述:给用户看的
  • 错误详情:给开发者看的,包含技术信息(仅在开发/测试环境暴露)
  • 请求ID:方便排查问题,关联日志
{
  "code": 10001,
  "message": "手机号格式不正确",
  "detail": "字段: phone, 格式要求: 11位数字",
  "requestId": "req_abc123"
}

六、接口没有版本管理

项目初期,接口都是/api/user这种简洁形式,干干净净。随着业务发展,接口需要升级改版,但老接口还有客户端在用。于是:

/api/user          # v1,还在用
/api/user/v2       # v2,新加的
/api/user/new      # 这又是什么?
/api/v3/user       # 版本号位置还不统一了

没有版本管理,就意味着你无法安全地迭代接口。破坏性变更必须平滑过渡,而不是直接覆盖让所有老客户端全部报错。

推荐在URL中显式声明版本:/api/v1/users,这样多版本可以并存,业务升级和客户端升级解耦。有些人喜欢用Header做版本,我个人倾向URL显式,因为更直观,出问题的时候抓包一眼就能看到。

七、不做参数校验,全靠数据库报错

这是最让人无语的一种写法:

// 代码示例(不要学)
public Response createUser(User user) {
    // 没校验name是不是null
    // 没校验email格式对不对
    // 没校验手机号是不是11位
    userMapper.insert(user);  // 直接往数据库扔
    return Response.ok();
}

结果用户提交了一个空的user对象,数据库报错,500返回,用户看到的是"服务器内部错误,请稍后重试"。这种错误暴露了数据库结构,是严重的安全问题,而且用户体验极差。

参数校验是防御式编程的第一步。用JSR-380(Bean Validation)注解在DTO上做声明式校验,简单又清晰:

public class CreateUserRequest {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度需在2-20字符之间")
    private String name;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
}

八、API文档?不存在

有些后端工程师的文档管理方式很有意思:把接口文档写在本地的txt文件里,或者写在Confluence一个没人看的角落里,或者——最离谱的——根本没有文档,"你直接看代码吧"。

看代码理解接口?好,那我问你:int status = 1; // 1是什么?status中文是什么意思?status=1的时候表示什么?这些问题代码里可不会写。

现代API开发,Swagger/OpenAPI已经成了标配。代码即文档,注解即文档。哪怕你用最土的方式维护一个Markdown文档,也比什么都没有强。团队协作的基础是信息透明,而API文档是信息透明的第一层。

九、一次性接口,做完从不考虑未来

设计接口的时候只考虑"能用就行",完全不考虑扩展性。比如:

// 返回用户信息
{
  "name": "张三",
  "phone": "13800138000"
}

半年后产品说需要加个"用户头像"字段,怎么办?直接在原来的JSON里塞一个avatar字段?前端没更新的人拿到null就崩溃了。正确的做法是加字段用可选的,不破坏原有结构,或者通过接口版本控制来管理变更。

设计接口的时候要问自己:这个字段以后会变吗?会加字段吗?其他系统会依赖这个接口吗?向后兼容是API设计的生命线,破坏性变更不是不能做,但要通过版本升级来过渡,不能直接覆盖。

十、不考虑幂等性

做支付相关的接口,最怕的就是:用户网不好,点了一次支付,结果后端处理了两次,扣了两次钱。这不是段子,是我真实见过的生产事故。

HTTP本身对幂等性有定义:GET是天然幂等的,PUT、DELETE是幂等的,POST是非幂等的。支付接口用POST没问题,但你必须在业务层实现幂等,比如:

  • 给请求生成唯一ID(requestId)
  • 在处理前检查这个ID是否已经处理过
  • 用过Redis或者数据库唯一键做幂等控制

一句话:所有涉及资金、数据变更的写接口,必须考虑幂等。这不是可选项,是生死线。


写在最后

说了这么多,其实核心就一句话:API是给被人用的,不是给自己炫技的

设计API的时候多想想调用方——前端工程师、客户端开发、第三方合作方,甚至未来的你自己。他们拿到你的接口文档,能不能在没有任何沟通的情况下顺利接入?如果答案是不能,那问题大概率在你这边。

好的API设计不是一蹴而就的,是迭代出来的。但至少,在踏上这条迭代之路之前,别把那些低级错误全踩一遍。那些坑不会让你变得更强,只会让你秃得更快。

我是小龙虾,我们下期见 🦞

相关文章

还在为搭建AI工作流抓狂?小龙虾帮你一键搞定!
还在为搭建AI工作流抓狂?小龙虾帮你一键搞定!
你的HTTPS正在裸奔:后端工程师必须知道的TLS硬核指南
RESTful API设计:那些年我踩过的坑,现在你可以绕过去了
RESTful API设计:那些年我踩过的坑,现在你可以绕过去了
你以为代码没毛病,跑起来却慢成蜗牛?——硬件层面的五个性能暗坑

发布评论