GET、POST、PUT、DELETE……HTTP 方法不只是动词,它们携带着“安全吗?幂等吗?能重试吗?”的约定。这篇先看一个真实服务器对每个方法的态度,再用一个“响应丢了”的模拟场景证明:为什么 GET 和 PUT 可以放心重试,而 POST 重试一次就可能把事情做两遍。
GET → HTTP/1.1 200 OK Allow: GET, HEAD body 589 字节 HEAD → HTTP/1.1 200 OK Allow: GET, HEAD body 0 字节 POST → HTTP/1.1 405 Method Not Allowed Allow: None body 570 字节 PUT → HTTP/1.1 405 Method Not Allowed Allow: None body 570 字节 DELETE → HTTP/1.1 405 Method Not Allowed Allow: None body 570 字节 OPTIONS → HTTP/1.1 405 Method Not Allowed Allow: None body 570 字节 PATCH → HTTP/1.1 405 Method Not Allowed Allow: None body 570 字节 TRACE → HTTP/1.1 405 Not Allowed Allow: None body 155 字节
example.com 只接受 GET 与 HEAD,其它方法一律 405。
GET:HTTP/1.1 200 OK,头部 12 个,消息体 589 字节;HEAD:HTTP/1.1 200 OK,头部 11 个,消息体 0 字节 值相同的头部:['accept-ranges', 'allow', 'alt-svc', 'cf-cache-status', 'connection', 'content-type', 'server'];不同或只在一边的头部(除 Date / Age / CF-RAY / Last-Modified):['transfer-encoding'] GET 有 Transfer-Encoding = chunked,HEAD 的 Content-Length = None
HEAD 给出与 GET 相同的头部、没有消息体,不需要 Transfer-Encoding。
urllib3 的默认重试方法集合(只有这些方法会在连接出错后自动重试):['DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'TRACE'] GET :前 2 次连接在服务器处理后被复位 → 客户端 最终成功,状态 200;服务器共收到 3 次该请求,连接数 3 PUT :前 2 次连接在服务器处理后被复位 → 客户端 最终成功,状态 200;服务器共收到 3 次该请求,连接数 3 POST :前 2 次连接在服务器处理后被复位 → 客户端 最终失败:ProtocolError;服务器共收到 1 次该请求,连接数 1 POST,强制允许 POST 重试(allowed_methods=None):客户端 最终成功,状态 200;服务器的账上 POST 被执行了 3 次——前 2 次的响应丢了,但业务已经执行了,重试就重复执行了(这里是 "扣款" 的重复)
GET 和 PUT 重试 3 次结果同 1 次;POST 重试会重复执行,默认客户端库不重试它。
小写 get → HTTP/1.1 400 Bad Request Allow: None OPTIONS * → HTTP/1.1 400 Bad Request Allow: None GET 带请求体(Content-Length: 5)→ HTTP/1.1 200 OK Allow: GET, HEAD DELETE / → HTTP/1.1 405 Method Not Allowed Allow: None
方法名区分大小写;OPTIONS * 被拒;GET 带请求体被忽略。