2. 接口设计基本原则:RESTful、HTTP方法、状态码

聊接口设计,绕不开RESTful。很多新人一听就觉得这是“标准规范”,得背下来。我个人倒是觉得,RESTful更像一套设计哲学,它告诉我们怎么用HTTP原生的东西把事情说清楚。

说白了,RESTful不是强迫你用哪个框架,而是让你尊重HTTP本身。你想想看,HTTP已经有了GET、POST、PUT、DELETE这些动词,还有状态码来表达结果,为什么还要自己再包一层“action=delete”的参数?

2.1 RESTful 设计原则

RESTful的核心就一句话:把每个“东西”抽象成资源,用URI定位,用HTTP方法操作。

  • 资源导向:设计的是资源,不是动作。比如 /secrets 代表一堆密钥,/secrets/123 代表某个密钥。
  • 无状态:服务端不存客户端上下文。每个请求都带齐所有需要的信息。
  • 统一接口:所有资源都通过相同的方法操作,不搞特殊。
  • 表现层分离:资源的结构和它的展示形式分开。你可以返回JSON、XML甚至YAML,但资源本身不变。

我当时的经验:有一次接手一个旧项目,他们把“获取密钥列表”设计成 POST /getSecrets。我改成了 GET /secrets,测试一跑,一堆客户端报错。嗯,这就是没理解“资源”的含义。

不过要注意,RESTful没有规定URI必须怎么拼,但业内有一些约定俗成的写法。比如用复数名词、小写、连字符代替下划线。

2.2 HTTP 方法的选择

HTTP方法说白了就是动词。对于Secrets接口,最常用的就这么几个:

方法 用途 幂等 安全
GET 读取资源 是 是
POST 创建资源 否 否
PUT 整体替换资源 是 否
PATCH 部分更新资源 否 否
DELETE 删除资源 是 否

小提示:幂等意味着你调一次和调十次,资源最终状态一样。PUT很典型——你把密钥正文改成"abc",重复调用还是"abc"。但POST每次都会新建一个,所以不幂等。

我曾经在一家云厂商做过SDK,发现很多人把更新密钥做成POST加参数 ?action=update。结果重复提交时生成了多条记录,生产环境直接炸了。后来强制要求必须用PUT或PATCH,才解决。

具体怎么选?我个人的做法是:

  • 读数据 → GET
  • 创建新资源(ID由服务端生成) → POST
  • 完整替换(ID客户端指定) → PUT
  • 部分更新 → PATCH
  • 删除 → DELETE

有个坑要注意:PATCH不是必须幂等的,但很多网关和中间件会把PATCH当非幂等处理。如果你的场景需要重试机制,建议用PUT代替PATCH,或者保证你的PATCH操作本身是幂等的。

2.3 状态码:让客户端看懂发生了什么

状态码是HTTP返回结果的第一行信息。很多开发者只记得200和500,其实多用状态码能省很多沟通成本。

我习惯把状态码分四组来记:

范围 含义 常见例子
2xx 成功 200 OK, 201 Created, 204 No Content
3xx 重定向 301 Moved Permanently, 304 Not Modified
4xx 客户端错误 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict
5xx 服务端错误 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

对于Secrets接口,有几个状态码特别重要:

  • 201 Created:创建成功时返回,附带Location头指向新资源。
  • 204 No Content:删除成功或更新成功但不需要返回体时用。
  • 400 Bad Request:请求体格式错误、缺少必填字段。
  • 401 Unauthorized:没带或无效。
  • 403 Forbidden:有但没权限读这个密钥。
  • 404 Not Found:密钥不存在。
  • 409 Conflict:版本冲突,比如并发更新时。
  • 429 Too Many Requests:限流触发。

避坑指南:我曾经见过一个团队,所有错误都返回200,然后在业务JSON里塞个“errorCode”。结果客户端全得解析JSON才知道请求失败。别这么做——直接用HTTP状态码表达结果,错误详情放body里。

举个实际例子,假设我们要创建一个密钥:

POST /secrets
Content-Type: application/json

{
  "name": "my-",
  "value": "sk-xxxx",
  "type": "api"
}

// 成功返回
201 Created
Location: /secrets/my-

如果密钥已经存在:

409 Conflict
{
  "error": "secret_already_exists",
  "message": "A secret with name 'my-' already exists"
}

2.4 核心知识图谱

下面这张图把上面三块内容串起来了。你会发现,RESTful原则是骨架,HTTP方法是动词,状态码是反馈。三者缺一不可。

RESTful接口设计核心知识图谱 RESTful接口设计核心原则 RESTful原则 HTTP方法 状态码 资源导向 无状态 统一接口 表现层分离 GET (读) POST (创建) PUT (整体替换) PATCH (部分更新) DELETE (删除) 2xx 成功 3xx 重定向 4xx 客户端错误 5xx 服务端错误 原则决定资源结构 → 方法决定操作类型 → 状态码反馈执行结果

我建议你在设计每个Secrets接口时,都先对照这张图过一遍:资源URI对吗?用了正确的方法吗?状态码能准确传达结果吗?跑通一遍流程,基本就不会出大问题。

2.5 小结

接口设计不是玄学,它就是选对动词、选对号码。RESTful给了我们一套通用的交流语言,HTTP方法定义了“做什么”,状态码告诉“结果怎样”。这三样东西搭起来,接口就清晰了。