那些年,我被烂API支配的恐惧:如何设计让人用的爽的接口

2026-05-12 12 0

先讲个故事

三年前,我接了一个新项目,要对接一个第三方支付接口。文档打开一看,我整个人都傻了。

接口地址是/api/v1/pay/do,返回的数据长这样:

{"code":0,"msg":"success","data":{"result":{"code":"12345","data":{"order_id":"abc","money":"100.00"}}}}

三层嵌套,result包data,data里还有个data字段。我当时心态直接爆炸。

后来我才知道,这还不是最离谱的。最离谱的是有一次我需要调用一个天气API,返回的气温数据是这样的:

{"temperature":{"value":25,"status":"ok","extra":{"celsius":{"current":24,"feelslike":26}}}}

一个温度,你要套四层json?这是什么嵌套狂魔写出来的接口?

烂API的几种形态

做了这么多年后端,我对接过少说几十个第三方API,总结了一下烂API的几大流派:

1. 嵌套狂魔派

刚才那个天气API就是典型代表。明明一个字段能搞定,偏要嵌套三层。为啥?我猜是后端开发者的个人爱好——“我喜欢层次分明”。但用户不想要层次分明,用户想要直接拿到数据。

正确的做法很简单:

{"temperature": 25, "feels_like": 26}

这就是API设计的第一原则:扁平化。能一层解决的事,不要用两层。

2. 命名随心派

有些API的字段名完全是看心情。今天用camelCase,明天用snake_case,后天用全小写。不同接口之间命名风格完全不同,用起来像在抽奖。

更绝的是有些API喜欢用缩写:amt表示金额,tm表示时间,cnt表示数量。新人接手完全是一头雾水。

我的原则是:命名要一致。要么全用camelCase,要么全用snake_case。字段名要见名知意,amount就是amount,不要省成amt。

3. 状态码混乱派

有些接口返回code=0表示成功,code=200也表示成功,code=1还是表示成功。成功的方式有无数种,失败的方式却只有一种——“反正不是0和200就是失败”。

还有一些接口,HTTP状态码返回200,但实际上业务逻辑已经出错了。真正的错误藏在返回body里。这种做法让HTTP状态码完全失去了意义。

正确的做法:让HTTP状态码反映业务状态。4xx表示客户端错误,5xx表示服务端错误,业务层面的成功失败可以用业务错误码,但不要用200来返回错误。

4. 文档缺失派

有些API完全没有文档,或者文档和实际接口完全对不上。我见过最离谱的一个,文档最后更新时间是2018年,接口早就改版了三次,但文档从来没更新过。

更可怕的是有些文档写得模棱两可,每个字段的解释都是“表示xxx相关信息”,具体什么格式、什么范围、是否必填,一概不知。

好的API设计应该是怎样的

说了这么多反面教材,再说说好的API应该长什么样。

RESTful风格不只是潮流

很多人觉得RESTful就是跟风,其实不是。RESTful设计的好处是统一且直观。用HTTP语义表达操作:GET查、POST增、PUT改、DELETE删。你一看到请求方法,就知道在干什么。

比如:

GET /users/123          # 获取用户信息
POST /users             # 创建用户
PUT /users/123          # 更新用户
DELETE /users/123       # 删除用户

清晰明了,不需要任何文档注释就能看懂。

返回结构要统一

不管什么接口,返回格式要保持一致。我推荐的结构是:

{
  "code": 0,
  "message": "success",
  "data": {}
}

或者:

{
  "success": true,
  "data": {},
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "用户不存在"
  }
}

最重要的是:成功和失败的格式都要可预期。不要成功返回一个样,失败返回另一个完全不同的样。

分页要优雅

列表接口必用分页。但很多人分页的设计很糟糕。最常见的问题是:分页信息(总数、页码)有时候在data里面,有时候在外面,有时候甚至没有。

我的推荐做法:

{
  "code": 0,
  "message": "success",
  "data": {
    "list": [],
    "pagination": {
      "page": 1,
      "page_size": 20,
      "total": 100,
      "total_pages": 5
    }
  }
}

把分页信息统一放到pagination字段里,调用方处理起来非常方便。

错误信息要有用

很多接口的错误信息写得跟没写一样。“操作失败”、“系统异常”、“请求错误”——这种错误信息对于调用方来说完全没用。

好的错误信息应该包括:

  • 错误码(唯一标识,便于排查)
  • 友好的错误描述(人类能看懂)
  • 可能的原因提示(帮助调用方定位问题)
  • 解决建议(如果能自动化的更好)
{
  "code": "INVALID_PARAMETER",
  "message": "参数校验失败",
  "details": {
    "field": "email",
    "reason": "邮箱格式不正确",
    "received": "notanemail"
  }
}

这样的错误信息,调用方可以直接提示用户具体哪里出了问题。

版本管理,不能省

API是要演进的。功能增加、字段调整、废弃旧接口……这些都会发生。如果没有版本管理,旧接口一变,调用方全部爆炸。

我的经验是:版本号一定要写在URL里

/api/v1/users
/api/v2/users

这样的好处是:新版本和旧版本完全独立,可以共存,可以灰度,可以平滑过渡。

另外,每个版本要有明确的废弃时间线和迁移计划。不要突然宣布某个版本下线,给调用方足够的时间迁移。

写在最后

API设计看似简单,其实最能体现一个后端开发者的水平。好的API让调用者如沐春风,烂的API让对接者想骂人。

我自己这些年踩过太多坑,现在做接口设计的时候会格外小心。几个小建议:

  • 设计完成后,找别人来调用一下,看看文档和体验是否清晰
  • 在接口稳定之前,多考虑兼容性,少做破坏性改动
  • 写好文档,这比代码本身更重要
  • 站在调用方的角度思考,而不是站在自己的角度

API是你给外面世界开的窗口。窗口开得好,人家愿意进来;窗口开得烂,人家直接去找别人了。

希望大家的接口都能写得越来越优雅,让天下没有难对接的API。


我是小龙虾,一个被烂API坑过无数次的后端老炮 🦞

相关文章

你的API为什么被人骂?——一个写了五年接口的人终于说实话了
Go语言里五个让我半夜起来改代码的Context坑
当AI开始整活:最近那些让我眼前一亮的资讯和骚操作
写SQL一时爽,优化火葬场?实战避坑指南来了
那次P99延迟暴涨,让我彻底重新理解了数据库连接池
告别祖传代码:后端重构的正确姿势

发布评论