状态码是 HTTP 里最浓缩的一个数字。客户端到底该怎么对待它们?404 要不要抛异常?429 / 503 之后什么时候重试?一个大请求体应不应该等服务器先说“继续”?这篇用真实的 httpbin 响应和本机服务器逐个验证。
码 原因短语 消息体字节 特殊头部
200 OK 0 {}
201 CREATED 0 {}
204 NO CONTENT 0 {}
301 MOVED PERMANENTLY 0 {'location': '/redirect/1'}
302 FOUND 0 {'location': '/redirect/1'}
304 NOT MODIFIED 0 {}
400 BAD REQUEST 0 {}
401 UNAUTHORIZED 0 {'WWW-Authenticate': 'Basic realm="Fake Realm"'}
403 FORBIDDEN 0 {}
404 NOT FOUND 0 {}
405 METHOD NOT ALLOWED 0 {}
409 CONFLICT 0 {}
410 GONE 0 {}
418 I'M A TEAPOT 135 {}
429 TOO MANY REQUESTS 0 {}
500 INTERNAL SERVER ERROR 0 {}
501 NOT IMPLEMENTED 0 {}
502 BAD GATEWAY 0 {}
503 SERVICE UNAVAILABLE 0 {}
504 GATEWAY TIMEOUT 0 {}2xx 成功、3xx 重定向、4xx 客户端的错、5xx 服务器的错;原因短语只给人看。
404:
requests.get:不抛异常,status_code = 404;r.ok = False;调用 raise_for_status() → 抛 HTTPError("404 Client Error: NOT FOUND for url: https://httpb")
urllib.request.urlopen:抛 HTTPError 404
urllib3.request(retries=False):不抛异常,status = 404
http.client:不抛异常,status = 404
500:
requests.get:不抛异常,status_code = 500;r.ok = False;调用 raise_for_status() → 抛 HTTPError("500 Server Error: INTERNAL SERVER ERROR for url: h")
urllib.request.urlopen:抛 HTTPError 500
urllib3.request(retries=False):不抛异常,status = 500
http.client:不抛异常,status = 500requests、urllib3、http.client 对 404 / 500 都不抛异常;urllib.request 会抛;不检查状态码是常见 bug。
429 + Retry-After: 2:客户端第 2 次请求拿到 200;两次请求到达服务器的间隔 2.01 秒(服务器要求等 2 秒) 503 前 4 次失败(没有 Retry-After),backoff_factor = 0.5:客户端第 5 次请求拿到 200;相邻请求的间隔(秒)[0.0, 1.0, 2.01, 4.01](指数退避:0 / 1.0 / 2.0 / 4.0 附近;urllib3 第一次重试不等) 不开重试时,同样的 503/200 序列只发 1 次请求:状态 200
有 Retry-After 就听服务器的;没有就指数退避。
不带 Expect:客户端把 5 MB 都发了(客户端写出 5000000 字节),服务器读了 5000000 字节后拒绝;客户端收到的第一行 'HTTP/1.1 401 Unauthorized' 带 Expect: 100-continue(未认证):服务器直接回 'HTTP/1.1 401 Unauthorized',客户端没有发请求体(写出 0 字节),服务器读到的请求体 0 字节——省下 5 MB
大请求体前先问服务器,被拒绝时省下整个请求体。