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 方法选择流程
下面这张图能帮你快速决定某个操作该用哪个方法。我自己画项目接口文档时,就常贴这么一张图在墙上。
这张图从「操作意图」出发,层层判断,最终落到具体方法。你可以把它当作设计接口时的 checklist。
3.9 几个容易忽略的细节
- 405 Method Not Allowed:如果客户端用了不支持的方法,应当返回 405 并在响应头
Allow中列出允许的方法。 - POST 的 201 vs 200:创建成功后建议返回
201 Created,并带上Location头指向新资源。如果只是触发某个动作(比如发送邮件),返回200 OK是合理的。 - PUT 要不要返回 body?:我一般返回更新后的资源(
200),如果资源没有变化也可以返回204。这取决于团队约定。 - 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"
}
最后说一句——方法选得对,文档少受罪。接口设计里,没有比「语义正确」更值钱的习惯。