RESTful API 基础:HTTP 方法、URI 设计、状态码

RESTful API 说起来很简单,就是把网络上的资源通过 URI 暴露出来,然后用 HTTP 方法操作它们。但我在十几年前刚接触这个概念时,踩了不少坑。

说白了,REST 不是一套协议,而是一组设计约束。你照着做,接口就清晰;你不照着做,后期维护就得抓狂。今天我把最核心的三块展开聊:

  • HTTP 方法 – 你到底想对资源做什么?
  • URI 设计 – 资源长什么样?怎么给它起名?
  • 状态码 – 服务器到底处理得怎么样了?

HTTP 方法:不只是 CRUD

大多数人把 HTTP 方法等同于数据库的增删改查(CRUD),这个理解方向是对的,但不够精确。RESTful API 里,每个方法都有明确的语义,用错了会让调用方困惑。

来看一张常见方法的对比表:

方法 语义 幂等? 安全? 典型场景
GET 获取资源 ✓ ✓ 查询列表、查看详情
POST 创建资源(或触发处理) ✘ ✘ 注册用户、提交订单
PUT 完全替换资源 ✓ ✘ 更新用户全部字段
PATCH 局部更新资源 ✘(大多数实现) ✘ 只改用户昵称
DELETE 删除资源 ✓ ✘ 删除文章
小提示:“幂等”的意思是,同一个请求发多次和发一次效果一样。这在网络重试场景下特别重要。我记得之前有个项目,用 POST 做支付回调,结果客户端重试导致重复扣款……后来改成幂等的 PUT 才解决的问题。

具体到 Secrets 管理接口,我习惯把密钥资源设计成这样:

GET    /secrets              # 获取密钥列表(元信息)
POST   /secrets              # 创建新密钥
GET    /secrets/{id}         # 获取单个密钥详情
PUT    /secrets/{id}         # 全量更新密钥内容
PATCH  /secrets/{id}         # 更新密钥的元数据(比如过期时间)
DELETE /secrets/{id}         # 删除密钥

你想想看,如果某个 API 用 GET 去修改资源,那语义就彻底乱套了。调用方不敢缓存,也不敢重试,整个系统的可预测性就打折扣。

URI 设计:命名即语义

URI 是资源的身份证。好的 URI 设计,看路径就能猜出资源和关系。我不喜欢那种 /api/getUserInfo?id=123 的风格,那更像是 RPC 而不是 REST。

几个我自己一直在用的原则:

  1. 用名词,不用动词。资源是名词(secrets, users, policies),动词留给 HTTP 方法。
  2. 用复数形式。/secrets 而不是 /secret,这样集合和单个实例都能统一表达。
  3. 用层级表达关联。比如 /secrets/{id}/versions 表示某个密钥的所有版本。
  4. 用查询参数做过滤、排序、分页。不要把它们塞进路径里。
  5. 避免太深的嵌套(超过三级)。我之前在项目里见过 /org/{orgId}/project/{projId}/env/{envId}/secret/{secretId},维护起来非常痛苦。
踩坑提醒:我曾经接手过一个旧项目,它的 URI 设计是 /getSecretsByUserId、/updateSecretValue 这种 RPC 风格。后来要加新功能,不得不新增几十个端点,整个接口文档又长又臭。后来全部重构了一遍,变成资源型 URI,工作量反而少了很多。别等到接口数量超过 50 个才想改,趁早。

状态码:沟通的语言

状态码不是随便给个 200 或 500 就完事了。正确的状态码能让调用方不用读响应体就明白发生了什么。这也是 RESTful 设计里“自描述消息”的一部分。

我一般把常用状态码分成五组:

范围 含义 常用码 我的经验
2xx 成功 200 OK, 201 Created, 204 No Content POST 创建成功后尽量用 201,别偷懒只给 200
3xx 重定向 301 Moved Permanently, 304 Not Modified REST API 里 3xx 用得少,但 304 配合缓存很实用
4xx 客户端错误 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 429 Too Many Requests 别把业务错误统统扔 400,分清 401/403/404 很重要
5xx 服务端错误 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable 500 应该留给意外,不要让预期内的校验错误也返回 500

举个例子:用户请求一个不存在的密钥,你返回 200 + 响应体里写 "status": "not found"。嗯,这样调用方就得解析 body 才知道结果。正确的做法是返回 404。一步到位。

核心要义:HTTP 方法 + URI + 状态码 三者搭配起来,就能构成一个自描述的 API。调用方只需要知道资源路径和方法,不用查阅文档就能正确使用。这恰恰是 RESTful 的精髓。

下面这张图把这三者的关系串起来——你在设计任何 REST 接口时,脑子里都应该过一遍这个流程:

RESTful API 核心要素流程 客户端 URI + HTTP 方法 GET /secrets/{id} 服务器处理 状态码返回 200 / 404 / 500 📌 URI 设计原则 • 名词复数,层级关系不超过三级 • 用查询参数做过滤/分页 • 避免动作名(不用 /getSecrets) 📌 状态码要点 • 2xx 成功,4xx 客户端错 • 5xx 服务端错,少用滥泛 500 • 不把错误信息藏进 200 body

你看,整个流程就是:客户端构造一个 URI + HTTP 方法请求,服务器处理完后返回带状态码的响应。每一步的规范程度,直接决定了接口的可用性和可维护性。

好了,这一轮的内容就到这。记住:方法选对、URI 起好、状态码给准,你的 RESTful API 就及格了。剩下的就是实践中不断打磨。