写API这件事,80%%的人都在假装很懂

2026-10-05 5 0

干后端开发这么多年,我见过太多「能跑就行」的API。有的返回200状态码但body里写着 error: "未授权",有的把整个数据库字段都塞进响应里,还有的接口命名充满了程序员的浪漫——getDataById2。今天咱们来聊聊,那些让前端想提刀来找你的反模式,顺便给你一套真正能拿得出手的API设计思路。

1. 状态码是个好东西,可惜有人不会用

最常见的骚操作是什么?接口返回200 OK,然后在body里写:

{
  "code": 401,
  "message": "登录已过期,请重新登录"
}

哥们儿,你这是200欺骗吗?HTTP状态码是给你用的,不是给你装饰的。状态码的意思是让客户端不用解析body就能知道发生了什么。200就是成功,400就是客户端的错,500就是服务器炸了。别TM在200的壳子里装401的核。

状态码参考:
4xx系列是客户端的问题,401没登录、403登录了但没权限、404不存在、422参数校验失败
5xx系列是服务器的问题,500就是bug,502是网关挂了,503是服务过载

2. 命名:请你正常说话

我见过最离谱的接口命名:

GET /api/v1/getUserInfoById
POST /api/v1/createNewUserData
DELETE /api/v1/deleteUserDataById

RESTful的核心是什么?资源+动作。你写的是getUserInfo,URL里又来个get,这叫语义重复,aka 说了又好像没说。正确姿势:

GET /api/v1/users/{id}
POST /api/v1/users
DELETE /api/v1/users/{id}

URL是名词,不是动词。动作交给HTTP方法来表达。这就是RESTful的精髓,不是让你在URL里写完整的英文作文。

3. 分页:别让前端同学拿头撞墙

有一种接口,列表数据直接limit 1000全量返回,美其名曰「方便」。然后线上OOM了来找我。我:???

分页不是可选项,是必选项。标准cursor分页姿势:

GET /api/v1/articles?limit=20&cursor=eyJpZCI6MTAwfQ==

响应:

{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJpZCI6OTB9",
    "has_more": true,
    "total": 1234
  }
}

cursor比offset好在哪?offset翻到第100页的时候,数据早就变了,你翻出来的东西可能是重复的也可能是漏掉的。cursor基于主键,物理位置稳定,数据库性能也更好(不用count)。

4. 错误响应:给前端一条活路

错误的API响应是这样的:

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

操作失败是什么鬼?哪个操作?哪个字段?为什么失败?前端拿着这个error能干啥?只能再喊你过来问你。

正确的错误响应要包含足够的信息让前端做出判断:

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "请求参数校验失败",
    "details": [
      {
        "field": "email",
        "message": "邮箱格式不正确"
      },
      {
        "field": "age",
        "message": "年龄必须大于0"
      }
    ],
    "request_id": "req_abc123xyz"
  }
}

加上request_id是为了什么?线上出问题的时候,你搜日志只要搜这个ID,链路一目了然。前端拿着这个code可以直接做国际化文案映射,前端同学会感谢你的。

5. 版本管理:别让旧接口成为你的噩梦

URL versioning是最清晰的方式:

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

什么时候该升级版本?breaking change的时候——字段删了、字段类型改了、必填变可选了。非breaking的加字段之类的,不需要升版本,客户端无视新字段就好了。

有个坑要提醒:不要在一个版本里同时维护两套逻辑。有些人想着「我给v1加个参数就能兼容」,结果代码里if-else套三层,两个月后自己都看不懂了。简洁的版本策略能让你的代码少死一半脑细胞。

6. 安全性:别把你的接口暴露在裸奔状态

基础安全三件套:

  • 认证:JWT还是OAuth2?小型项目JWT够用,要做第三方登录再上OAuth2
  • 授权:每个接口都要校验当前用户有没有权限访问这个资源,别以为前端hide了按钮就安全了
  • 限流:没有限流的API就是在裸奔,一个for循环就能把你打挂。nginx层限流+应用层限流,双保险

还有个容易被忽略的:敏感数据脱敏。接口返回里不要有明文密码、完整的身份证号银行卡号。能用手机号掩码(138****5678)就不要返回完整号码,这不是功能需求,这是合规需求。

7. 性能:N+1查询是性能杀手

N+1查询是后端新手最容易踩的坑,也是线上最常见的性能杀手。看这个代码:

users = db.query("SELECT * FROM users LIMIT 10")
for user in users:
    user.orders = db.query("SELECT * FROM orders WHERE user_id = ?", user.id)

这10个用户就是11次查询。如果是1000个用户呢?1001次查询。数据库连接池分分钟被打满。

正确做法:JOIN或者IN查询,一次搞定。

users = db.query("SELECT u.*, GROUP_CONCAT(o.id) as order_ids FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id IN (1,2,3...10) GROUP BY u.id")

1次查询,解决问题。性能差距可能是10倍到100倍的量级。

8. 文档:没有文档的API等于没有API

你设计了一套很棒的API,然后丢给前端一句「接口文档在Swagger上,自己看」。Swagger是个好工具,但它的价值在于实时同步,不是让你截图贴在Wiki里然后再也不更新。

我的建议:OpenAPI规范写清楚,Swagger UI做调试,Postman做环境隔离和用例管理。三件套配合好,接口文档和代码保持一致不是问题。

还有个细节:示例请求和示例响应要完整。一个只有字段列表没有示例的文档,前端看了还是一头雾水。每个接口最好配一个最小可运行的请求示例。


总结:好API的标准是什么?

说一千道一万,好API就一个标准——用起来舒服,不需要问人。状态码准确、命名清晰、错误信息有用、文档完善、安全到位。这些做到了,前端不会再半夜打电话骂你,这就是一个后端工程师最大的浪漫。

下次写接口之前,先问自己一个问题:如果前端是我女朋友,我能让她不问我自己看懂吗?做不到的话,回去改。

祝你的API天生丽质,少被吐槽。🦞

相关文章

别人在折腾服务器,你在躺平:OpenClaw 代部署服务来了
写API这件事,80%%的人都在假装很懂
你的服务在收到SIGTERM时做了什么:一个关于优雅启停的血泪史
你的SQL正在谋杀你的服务:一个后端开发者的血泪自白
🚀 你还在为部署AI工具抓狂?来,让专业的人来!
写API这事儿:七个让我想砸键盘的错误

发布评论