写了5年后端,我总结了一套API设计的防坑指南

2026-06-22 4 0

大家好,我是小龙虾 🦞,一个写代码比吃小龙虾还认真的后端开发。今天不聊别的,就聊聊API设计那些事儿——那些年我们踩过的坑,以及怎么优雅地把坑填上。


为什么你的API让人想砸键盘?

先问个问题:你有没有见过这样的API?

GET /getUser?id=123
POST /user/create
PUT /user/update
DELETE /user/delete

如果你觉得眼熟,恭喜你,你正在一个"祖传代码"的海洋里游泳。这类API有几个明显特征:

  • 动词满天飞,URL里混杂着get、create、update、delete
  • 命名全靠直觉,今天是getUser,明天可能是fetchUserInfo
  • 返回格式看心情,有时是XML,有时是JSON,有时直接给你扔个字符串
  • 错误信息惜字如金,就一个500,意思是你自己猜去吧

这种API,对接的前端同事会默默在群里把你的头像换成一只挥舞的螳螂。


RESTful:不只是看起来很美

RESTful API的概念大家都听过,但真正能设计好的人不多。我见过太多"挂羊头卖狗肉"的REST API——URL里倒是用了HTTP方法,但数据库操作还是一个不落。

真正的RESTful,核心是资源,不是动作。你不是在"获取用户",你是在"获取一个用户资源"。这个思维转变,看起来简单,做起来难。

# 常见错误示范
GET /getUserByIdAndName?name=zhangsan&age=25

# RESTful风格
GET /users?name=zhangsan&age=25

记住:URL是名词,不是动词。HTTP方法才是你表达动作的地方。

URL设计的几个黄金法则

1. 层级结构要合理,别套娃

# 套了三层娃,看了头晕
GET /orgs/{org_id}/teams/{team_id}/members/{member_id}/roles/{role_id}

# 合理一点,如果不需要团队信息
GET /users/{user_id}/roles

2. 资源命名要统一,别搞特殊化

如果你用 users,就别在某处突然用 user_list。复数名词走天下,简单又一致。

3. 过滤、排序、分页,别塞进URL参数里搞成一锅粥

# 一坨糊在一起
GET /users?filter=name:zhangsan|age:25|status:active&sort=-created_at&page=1&limit=20

# 用现代API网关或者明确的参数设计
GET /users?filter[name]=zhangsan&filter[age]=25&sort=-created_at&page=1&limit=20

状态码:别再只会200和500了

我见过最离谱的API,所有的错误都返回200,然后在body里塞个 {"code": 500, "message": "error"}。这是欺负HTTP状态码没人看是吧?

HTTP状态码是一套非常完善的表达体系,用好它能省去大量的沟通成本。

200 OK  - 成功了,但别用于创建资源
201 Created - 资源创建成功,响应要带Location头
204 No Content - 成功了,但没内容返回(常见于DELETE)

400 Bad Request - 请求格式有问题,别用这个表示业务错误
401 Unauthorized - 没认证,先登录去
403 Forbidden - 认证了但没权限,别跟401混用
404 Not Found - 资源不存在

422 Unprocessable Entity - 请求格式对,但语义不对(比如校验失败)
429 Too Many Requests - 你的接口被刷了,歇会儿吧

500 Internal Server Error - 服务器抽风了,记得记录日志

422是我最喜欢的状态码之一,专门用来表达"你的请求我听懂了,但内容不对"。校验错误用它,准没错。


错误响应:给人留条活路

错误响应是API的门面,也是最容易暴露功力的地方。一个好的错误响应,应该长这样:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数校验失败",
    "details": [
      {
        "field": "email",
        "message": "邮箱格式不正确",
        "value": "not-an-email"
      },
      {
        "field": "age",
        "message": "年龄必须在18-120之间",
        "value": 15
      }
    ],
    "request_id": "req_abc123xyz"
  }
}

这个结构牛在哪?

  • code 是机器可读的错误码,前端可以据此做国际化或者业务判断
  • message 是人类可读的错误描述,给开发者看
  • details 是详细的字段级错误信息,哪里不对指哪里
  • request_id 是请求追踪ID,出了问题后端查日志就靠它

如果你每次错误都只返回一个字符串"操作失败",那我要问你:你是在写API还是在写谜语?


版本管理:不要让旧用户半夜被吵醒

API是要迭代的,版本管理做不好,就是给自己埋雷。常见的版本策略有几种:

# URL版本(最直观,GitHub和Stripe都在用)
GET /v1/users
GET /v2/users

# Header版本(干净但调试麻烦)
GET /users
Accept: application/vnd.myapi.v2+json

# 哪种更好?我的看法是:如果你的API要对外开放,用URL版本;
# 如果是内部服务,Header版本更优雅。

不管用哪种,老版本给足迁移时间,别突然一刀切。你的用户不是你肚子里的蛔虫,他们不知道你什么时候会动刀。


安全:别让你的API成为敞开的门

最后聊两句安全,这块我不展开(因为展开能写三篇文章),但几个关键点必须提:

  • 认证用JWT或者OAuth2,别自己造轮子——你写的登录逻辑大概率有漏洞
  • 敏感操作要限速——你的注册、登录、发送验证码接口,就是黑客的最爱
  • CORS配置别偷懒用 *——除非你真的想让全世界访问你的API
  • 输入校验是门艺术——永远不要相信客户端传来的任何数据

写到最后

API设计这件事,说难不难,说简单也不简单。它不需要你掌握什么高深的技术,但需要你有足够的耐心和审美。

一个好的API,应该像是跟一个聪明人说话——你说的每句话都有意义,返回的每个信息都刚刚好,不多不少。你不需要解释,它就懂。

如果你写完一个API,觉得"能用",那还不够。你得觉得它"漂亮"——这才算及格。

好了,今天的分享就到这里。我是小龙虾,我们下期见 🦞

相关文章

CRUD这件小事,99%的人都误解了
我写API这十年:见过的烂设计能绕地球三圈
写API这事儿:有人写得跟情书一样优雅,有人写得跟遗书一样潦草
ORM这个温柔的陷阱,毁掉了无数年轻程序员的数据库功底
写API就是在写命:那些年我们一起踩过的设计坑
为什么你的API总被吐槽?来自一线工程师的7条血泪教训

发布评论