3. HTTP 请求方法详解(GET、POST、PUT、DELETE 等)

咱们做接口设计,绕不开的就是 HTTP 请求方法。说白了,它就是客户端告诉服务端「你想干什么」的指令。方法选不对,轻则语义混乱,重则埋下安全隐患。我记得早些年接手过一个老系统,所有操作全用 GET,连删除也用 GET——那叫一个酸爽。

核心原则:每个 HTTP 方法都有明确的语义和约束。选对方法,比写好代码更重要。

3.1 GET — 查,就完了

GET 用于获取资源。它应该是幂等且安全的——重复请求不会改变服务器状态,也不会产生副作用。我个人习惯把 GET 叫「只读查询」。比如获取用户列表、获取订单详情,都用 GET。

  • 参数通过 URL 传递(query string 或 path variable)
  • 响应体可以缓存
  • 不能有请求体(尽管技术上允许,但没人这么做)

我曾经踩过坑:在 GET 接口里偷偷改了数据库中的「点击量」字段。结果爬虫一跑,点击量暴涨。后来才意识到,幂等性不是说说而已,任何副作用的 GET 都是反模式。

// ✅ 正确:获取用户
GET /users/123
// ❌ 错误:带有操作副作用
GET /articles/456?action=increase_view

3.2 POST — 创建,也可以做更多

POST 是「非幂等」的典型。每次请求都可能创建新资源,或者触发业务处理。我常用 POST 来做表单提交、文件上传、复杂查询(当查询参数过长时)。

  • 参数放在请求体里(JSON、form-data 等)
  • 响应状态码常用 201 Created 或 200 OK
  • 注意防重放攻击——你想想看,用户不小心点了两次提交,结果生成两笔订单,多闹心

经验:POST 和 GET 不是「隐藏参数」和「显式参数」的区分。语义才是第一位的。别为了省事把 GET 改成 POST 就完事。

// 创建新用户
POST /users
Content-Type: application/json
{
  "name": "张三",
  "email": "zhangsan@example.com"
}
// 响应:201 + Location 头
Location: /users/7

3.3 PUT — 全量替换,幂等

PUT 用于完整更新一个资源。客户端要提供资源的所有字段,服务端用新数据完全替换旧数据。它必须是幂等的——重复执行结果一致。我一般只在「确定客户端拥有完整资源数据」的场景下用 PUT。

// 全量更新用户(必须传所有字段)
PUT /users/123
Content-Type: application/json
{
  "name": "李四",
  "email": "lisi@example.com",
  "phone": "13800138000"
}

如果只更新某个字段,别用 PUT。你可能会问:「那用什么?」答案就在下面。

3.4 PATCH — 部分更新,非幂等

PATCH 和 PUT 的区别,说白了就是一个「局部修改」,一个「全量覆盖」。我习惯把 PATCH 用在修改密码、更新头像这类场景。注意,PATCH 不一定幂等——如果两次 PATCH 操作的是不同属性,结果可能不同。

我曾经遇到的坑:团队里有人把 PATCH 当 PUT 用,每次全量覆盖,结果字段丢失不报错。后来我们在代码里加了 ValidateNoExtraFields 中间件才避免。

// 仅修改用户邮箱
PATCH /users/123
Content-Type: application/json
{
  "email": "newemail@example.com"
}

3.5 DELETE — 删除,注意幂等

DELETE 用于删除资源。理论上它也应该是幂等的——第一次删除返回 200 或 204,第二次再删同一个资源,应返回 404,但不要报错。我建议返回 204 No Content,干净利落。

  • 删除后资源不可访问
  • 通常不需要请求体
  • 对于「软删除」,可以考虑用 PATCH 或 POST 来标记状态
// 删除用户
DELETE /users/123
// 响应:204 No Content

3.6 HEAD 与 OPTIONS — 辅助方法

HEAD 和 GET 几乎一样,只是不返回响应体。我常用它来检查资源是否存在、获取元数据(如 Content-Length)。OPTIONS 则用于「询问」服务器支持哪些方法。这在跨域请求(CORS)中特别常见。

// 询问 /api/users 支持的方法
OPTIONS /api/users
// 响应:
Allow: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS

3.7 方法对比速查表

方法 语义 幂等 安全(只读) 请求体 响应体
GET 获取资源 是 是 无 有
HEAD 获取元数据 是 是 无 无
OPTIONS 查询可用方法 是 是 可选 有
POST 创建/触发操作 否 否 有 有
PUT 全量更新 是 否 有 可选
PATCH 部分更新 否 否 有 可选
DELETE 删除资源 是 否 无 可选

3.8 方法选择流程

下面这张图能帮你快速决定某个操作该用哪个方法。我自己画项目接口文档时,就常贴这么一张图在墙上。

HTTP 请求方法选择流程图 开始:确定操作 是查询/获取资源? GET 幂等·安全 创建新资源? POST 非幂等 更新已有资源? 全量替换? PUT 幂等 PATCH 非幂等 删除资源? DELETE 幂等 HEAD 青色为判断节点,彩色为方法节点

这张图从「操作意图」出发,层层判断,最终落到具体方法。你可以把它当作设计接口时的 checklist。

3.9 几个容易忽略的细节

  1. 405 Method Not Allowed:如果客户端用了不支持的方法,应当返回 405 并在响应头 Allow 中列出允许的方法。
  2. POST 的 201 vs 200:创建成功后建议返回 201 Created,并带上 Location 头指向新资源。如果只是触发某个动作(比如发送邮件),返回 200 OK 是合理的。
  3. PUT 要不要返回 body?:我一般返回更新后的资源(200),如果资源没有变化也可以返回 204。这取决于团队约定。
  4. PATCH 的 Content-Type:推荐用 application/json-patch+json 或 application/merge-patch+json,别自己发明格式。

小技巧:在 API 网关层统一打印请求方法和响应状态码,排查问题时一眼就能看出「谁用了什么方法」。我在项目里加了这样的日志,调试效率直接翻倍。

// 规范响应示例
HTTP/1.1 201 Created
Location: /users/7
Content-Type: application/json

{
  "id": 7,
  "name": "张三",
  "email": "zhangsan@example.com",
  "createdAt": "2024-03-15T10:30:00Z"
}

最后说一句——方法选得对,文档少受罪。接口设计里,没有比「语义正确」更值钱的习惯。