3. HTTP 请求方法:语义与使用场景
HTTP 请求方法,说白了就是客户端告诉服务器「我想干什么」。一共定义了九种方法,但日常开发中常用的也就那么四五个。我刚开始接触 HTTP 时,总觉得 GET 和 POST 够用了,直到有一次在项目中设计 RESTful API,才发现用错方法会带来多大的麻烦。
咱们一个一个来看。
3.1 GET:获取资源
GET 是最常用的方法。它的语义很简单——请求服务器返回某个资源。浏览器地址栏输入 URL 回车,就是一次 GET 请求。
核心特征:
- 幂等:多次请求结果相同
- 安全:不会修改服务器状态
- 可缓存:浏览器和代理服务器都会缓存 GET 响应
- 请求体为空:参数通过 URL 查询字符串传递
我个人习惯把 GET 用于「只读」操作。比如获取用户列表、查询订单详情、加载文章内容。嗯,这里要注意——绝对不要用 GET 传递敏感信息。URL 会被记录在服务器日志、浏览器历史、代理缓存里,密码明文出现在 URL 上?那等于公开了。
我曾经踩过的坑:有个同事把删除操作做成了 GET 请求。结果搜索引擎爬虫路过,把整张表删了个干净。从那以后,我团队里谁再用 GET 做写操作,代码 review 直接打回。
3.2 POST:创建资源 / 提交数据
POST 的语义是「提交数据给服务器处理」。它不幂等,也不安全,更不可缓存。你想想看,提交两次表单和提交一次,结果能一样吗?
POST 的请求体可以很大,没有长度限制(理论上)。表单提交、文件上传、JSON 数据推送,都用 POST。
| 对比项 | GET | POST |
|---|---|---|
| 幂等性 | 是 | 否 |
| 安全性 | 是 | 否 |
| 可缓存 | 是 | 否 |
| 请求体 | 无 | 有 |
| 参数位置 | URL 查询字符串 | 请求体 |
| 数据长度 | 有限制(浏览器/服务器限制) | 无限制 |
| 典型场景 | 查询、浏览 | 创建、提交 |
3.3 PUT:更新 / 替换资源
PUT 的语义是「把资源完全替换成请求体里的内容」。它是幂等的——你 PUT 同一个数据十次,结果和一次一样。
我在项目中遇到过一个问题:很多人分不清 PUT 和 POST。其实很简单——PUT 是「全量替换」,POST 是「追加创建」。比如更新用户信息,你用 PUT 就得把整个用户对象传过去,少一个字段,那个字段就被清空了。
// PUT 请求:全量替换
PUT /users/123
Content-Type: application/json
{
"name": "张三",
"email": "zhangsan@example.com",
"age": 28
}
// 如果只传 name,age 会被清空
PUT /users/123
Content-Type: application/json
{
"name": "李四"
}
// 结果:name 变成 "李四",email 和 age 变成 null
小技巧:如果你只想更新部分字段,别用 PUT,用 PATCH。PUT 是「换整辆车」,PATCH 是「换个轮胎」。
3.4 DELETE:删除资源
DELETE 的语义很直白——告诉服务器「把这个资源删掉」。它是幂等的,但注意:幂等不代表每次响应码都一样。第一次删除返回 200,第二次资源不存在返回 404,但服务器状态没变——资源确实没了。
我曾经见过一个设计:DELETE 请求返回 200 和删除的数据体。这其实不太对。按照 HTTP 规范,DELETE 成功应该返回 204 No Content,表示「操作完成,没有内容返回」。
3.5 HEAD:获取响应头
HEAD 和 GET 几乎一样,唯一的区别是——服务器不返回响应体。你想想看,什么时候需要这个?
- 检查资源是否存在(状态码 200 vs 404)
- 获取资源元信息(Content-Type、Content-Length)
- 测试链接有效性
- 检查资源是否被修改(Last-Modified、ETag)
我在做爬虫时经常用 HEAD 先探路。如果返回 404,就不浪费带宽去下载整个页面了。
3.6 OPTIONS:获取服务器支持的方法
OPTIONS 的语义是「问服务器你支持哪些方法」。服务器会在 Allow 响应头里列出允许的方法列表。
// 请求
OPTIONS /api/users HTTP/1.1
Host: example.com
// 响应
HTTP/1.1 204 No Content
Allow: GET, POST, PUT, DELETE, OPTIONS
这个方法和 CORS(跨域资源共享)关系密切。浏览器在发起跨域请求前,会先发一个 OPTIONS 预检请求,确认服务器是否允许跨域。如果服务器不配合,浏览器就直接拦截了。
3.7 PATCH:部分更新资源
PATCH 是 PUT 的补充。它只更新资源的部分字段,而不是全量替换。前面说了,PUT 是「换整辆车」,PATCH 是「换个轮胎」。
// PATCH 请求:只更新 email
PATCH /users/123
Content-Type: application/json
{
"email": "newemail@example.com"
}
// 结果:只有 email 变了,name 和 age 保持不变
PATCH 不是幂等的。为什么?因为部分更新的语义取决于服务器实现。比如「给用户积分加 10 分」,执行两次和一次结果当然不一样。
3.8 TRACE:回显请求
TRACE 用于诊断。服务器收到 TRACE 请求后,会把请求原封不动地返回给客户端。这样客户端就能看到请求经过中间代理后有没有被篡改。
说实话,这个我几乎没用过。因为 TRACE 存在安全风险(容易引发跨站追踪攻击),大多数生产环境都禁用了它。
3.9 CONNECT:建立隧道
CONNECT 用于建立网络隧道,最常见的就是 HTTPS 代理。客户端通过 CONNECT 告诉代理服务器「帮我连接到目标服务器」,代理建立 TCP 连接后,后续数据直接透传。
你想想看,浏览器访问 HTTPS 网站时,代理服务器怎么知道该转发给谁?就是靠 CONNECT 方法告诉它的。
3.10 方法对比总览
| 方法 | 语义 | 幂等 | 安全 | 可缓存 | 请求体 |
|---|---|---|---|---|---|
| GET | 获取资源 | ✓ | ✓ | ✓ | 无 |
| POST | 创建/提交 | ✗ | ✗ | ✗ | 有 |
| PUT | 全量替换 | ✓ | ✗ | ✗ | 有 |
| DELETE | 删除资源 | ✓ | ✗ | ✗ | 可有可无 |
| HEAD | 获取响应头 | ✓ | ✓ | ✓ | 无 |
| OPTIONS | 查询支持方法 | ✓ | ✓ | ✗ | 无 |
| PATCH | 部分更新 | ✗ | ✗ | ✗ | 有 |
| TRACE | 回显诊断 | ✓ | ✓ | ✗ | 无 |
| CONNECT | 建立隧道 | ✗ | ✗ | ✗ | 无 |
3.11 知识体系图
下面这张图帮你理清九种方法的关系和分类:
3.12 实际开发中的选择建议
说了这么多理论,落到实际项目里该怎么选?我总结几条经验:
- 查询用 GET——别问,问就是 GET。参数多?用查询字符串。参数敏感?那是设计问题,不是方法问题。
- 新增用 POST——除非你能保证客户端知道资源的最终 URI,那可以用 PUT。
- 全量更新用 PUT——客户端必须传完整资源对象。
- 部分更新用 PATCH——只传要改的字段。
- 删除用 DELETE——别整幺蛾子,别用 GET 删东西。
- 预检用 OPTIONS——CORS 场景下浏览器会自动发,你基本不用手动处理。
我的习惯:设计 RESTful API 时,我会在接口文档里明确标注每个端点的 HTTP 方法、是否幂等、请求体格式。这样前后端联调时少了很多扯皮。你想想看,如果后端说「这个接口用 POST」,前端问「那幂等吗」,答不上来就尴尬了。