4、HTTP请求方法详解:GET、POST、PUT、DELETE……
HTTP请求方法,说白了就是客户端告诉服务器“我想干什么”。一共定义了9种,但日常开发中常用的也就那么四五个。我刚开始做接口联调时,经常把POST和PUT搞混,直到有一次在生产环境上把数据重复插入了两遍……嗯,从那以后我对每个方法的语义都特别较真。
咱们一个一个来看,每种方法我都会结合抓包的实际场景讲。
4.1 GET方法——最常用的“查”
GET就是去服务器拿资源。浏览器输入网址、点击链接、加载图片,全是GET。它的特点是:幂等、安全、可缓存。
- 幂等:你请求100次和请求1次,服务器上的资源状态不变。
- 安全:不会修改服务器上的数据。
- 可缓存:浏览器和代理服务器可以缓存GET响应。
抓包要点:GET请求没有请求体,参数全在URL的查询字符串里。你在Wireshark里看,一个GET包的结构就是:请求行 + 请求头 + 空行。干净利落。
GET /api/users?id=123&page=1 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: application/json
我曾经踩过的坑:URL长度限制。虽然HTTP协议本身没规定GET的URL长度,但浏览器和服务器都有隐含限制。IE限制2083字符,Nginx默认是8K。有一次我传了个Base64编码的大参数,结果被截断了,后端收到的是残缺数据。排查了半天才发现是URL超长。
4.2 POST方法——最常用的“增改”
POST用来提交数据给服务器,通常用于创建资源。它的核心特征是:非幂等、不安全、不可缓存(默认)。
你提交表单、上传文件、发JSON数据,基本都是POST。数据放在请求体里,Content-Type告诉服务器怎么解析。
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 45
{"name":"张三","age":28,"email":"zhang@example.com"}
| 属性 | GET | POST |
|---|---|---|
| 请求体 | 无 | 有 |
| 幂等性 | 是 | 否 |
| 安全性 | 是 | 否 |
| 可缓存 | 是 | 仅支持特定条件 |
| URL参数 | 查询字符串 | 请求体 |
| 数据长度 | 有限制 | 理论上无限制 |
个人习惯:我写RESTful API时,创建资源用POST,更新用PUT,部分更新用PATCH。这样语义清晰,别人接手代码一看就懂。
4.3 PUT方法——全量替换
PUT是幂等的。你发一次和发十次,最终资源状态是一样的。它要求客户端提供完整的资源表示,服务器用这个表示替换已有的资源。
PUT /api/users/123 HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 60
{"id":123,"name":"李四","age":30,"email":"li@example.com"}
注意:如果资源不存在,有些实现会创建它(upsert),有些会返回404。我个人建议严格遵循语义——PUT只做替换,创建用POST。
4.4 DELETE方法——删除资源
DELETE也是幂等的。第一次删除返回200或204,第二次再删同一个资源,服务器应该返回404(资源已不存在),但资源状态没变——还是不存在。
DELETE /api/users/123 HTTP/1.1
Host: example.com
我曾经遇到的坑:DELETE请求的请求体。有些开发者习惯在DELETE里带请求体传参数,但很多代理服务器和网关会丢弃DELETE的请求体。我建议参数放URL路径或查询字符串里,别放body。
4.5 HEAD方法——只要响应头
HEAD和GET几乎一样,唯一的区别是服务器不返回响应体。用来检查资源是否存在、获取元数据(Content-Length、Content-Type、Last-Modified等)。
我常用HEAD做资源探测。比如下载大文件前,先发个HEAD看看文件大小,再决定要不要下载。
HEAD /bigfile.zip HTTP/1.1
Host: download.example.com
// 响应
HTTP/1.1 200 OK
Content-Length: 1048576000
Content-Type: application/zip
Last-Modified: Mon, 15 Jan 2024 10:30:00 GMT
4.6 OPTIONS方法——探路先锋
OPTIONS用来查询服务器支持哪些方法。响应头Allow字段会列出允许的方法列表。这在CORS(跨域)场景下特别常见——浏览器发预检请求时用的就是OPTIONS。
OPTIONS /api/users HTTP/1.1
Host: example.com
Origin: https://frontend.com
Access-Control-Request-Method: POST
// 响应
HTTP/1.1 204 No Content
Allow: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
避坑指南:OPTIONS请求不会携带认证信息(Cookie、Authorization头)。如果你的API需要鉴权,别在OPTIONS上做校验,否则预检请求会失败。
4.7 PATCH方法——局部更新
PATCH和PUT的区别:PUT是全量替换,PATCH是局部修改。你只传要改的字段,服务器只更新这些字段。
PATCH /api/users/123 HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 18
{"age": 31}
PATCH不是幂等的。比如你发“给age加1”,发两次结果不一样。这一点要特别注意。
4.8 TRACE方法——诊断回显
TRACE让服务器把收到的请求原样返回。用来诊断请求在传输过程中有没有被篡改(比如被代理服务器修改了头)。
不过说实话,生产环境基本不用。因为TRACE存在安全风险(跨站追踪攻击),大多数服务器默认关闭它。
TRACE / HTTP/1.1
Host: example.com
X-Forwarded-For: 192.168.1.1
// 响应
HTTP/1.1 200 OK
Content-Type: message/http
TRACE / HTTP/1.1
Host: example.com
X-Forwarded-For: 192.168.1.1
4.9 CONNECT方法——隧道代理
CONNECT用于建立隧道,最常见的就是HTTPS代理。客户端通过CONNECT告诉代理服务器“我要连接到目标服务器的443端口”,代理建立TCP隧道后,客户端和服务器直接进行TLS握手。
CONNECT example.com:443 HTTP/1.1
Host: example.com:443
// 响应
HTTP/1.1 200 Connection Established
之后所有数据都在隧道里加密传输,代理只负责转发,看不到内容。
4.10 抓包实战建议
最后分享几个抓包时的实用技巧:
- 过滤方法:在Wireshark里用
http.request.method == POST快速定位特定方法的请求。 - 关注响应码:GET成功返回200,POST创建成功返回201,DELETE成功返回204,PUT成功返回200或204。
- 注意缓存:GET请求会被浏览器缓存,调试时记得勾选“Disable cache”。
- OPTIONS预检:看到非简单请求(比如带自定义头、Content-Type为application/json)先发OPTIONS,这是CORS机制,别慌。
我个人习惯:每次接手新项目,第一件事就是抓包看接口用了哪些方法。如果全是POST,那这个项目的RESTful风格基本没落地。如果GET、POST、PUT、DELETE分得很清楚,说明团队有规范,代码质量通常不会太差。