(十六)状态、请求失败与数据失败
1. 把状态对应到下一步动作
| 状态类别 | 处理过程中的含义 | 本例动作 |
|---|---|---|
| 2xx | 服务器完成请求 | 继续检查格式、解析与校验记录 |
| 3xx | 资源位置或处理方向发生变化 | 查看 Location 与客户端重定向设置 |
| 4xx | 这份请求无法按要求完成 | 检查路径、参数、身份与权限 |
| 5xx | 服务器处理失败 | 保留状态与错误,按业务决定稍后重试 |
200 只说明这次 HTTP 请求成功,正文仍可能是 HTML、损坏的 JSON,或字段不符合书籍格式。401 表示需要认证,403 表示请求被拒绝,404 是目标没有找到,429 常用于请求过多;500、503 则提示服务器处理或当前服务状态出现问题。HTTP 状态
# 项目文件:demos/http_errors.py
# HTTPError 是带状态响应的错误;解析失败则发生在请求已经成功之后。
import json
from urllib.error import HTTPError, URLError
from urllib.request import urlopen
from labserver import running_server
if __name__ == "__main__":
with running_server() as server:
try:
with urlopen(server.base_url + "/api/books/999", timeout=1) as response:
print(json.load(response))
except HTTPError as error:
with error:
print("HTTP 状态", error.code, json.load(error)["error"])
except URLError as error:
print("连接请求失败", error.reason)
with urlopen(server.base_url + "/api/malformed", timeout=1) as response:
print("HTTP 成功", response.status)
try:
json.load(response)
except json.JSONDecodeError:
print("正文不是完整 JSON")依次看到 404、HTTP 成功 200 和正文解析失败。HTTPError 也是 URLError 的子类,先接收它,才能保留状态与错误正文;建立连接等环节失败则没有这份成功响应可解析。urllib 错误类别
2. 一次调用超时不等于服务器撤销操作
urlopen(..., timeout=1) 给阻塞网络操作设置超时,不是为整套“重定向、读取、解析与保存”统一计算一秒总预算。异步客户端稍后还会分别设置连接、读取等等待,以及整批请求的总时间范围。
客户端超时或取消等待时,服务器可能已经处理了请求。读取书目失败后可以按需要再次读取;新增阅读日志的 POST 若已成功处理,只是响应没被客户端收到,直接重复发送会新增另一条。
# 项目文件:demos/http_post_twice.py
# 两次 POST 是两次新增操作;相同请求体不会自动让服务器当作同一条记录。
import json
from urllib.request import Request, urlopen
from labserver import running_server
if __name__ == "__main__":
with running_server() as server:
data = json.dumps({"book_id": 1, "minutes": 35}).encode("utf-8")
for _ in range(2):
request = Request(
server.base_url + "/api/logs",
data=data,
method="POST",
headers={"Content-Type": "application/json"},
)
with urlopen(request, timeout=1) as response:
print(response.status, json.load(response)["log_id"])得到 201 1、201 2。之后实现写操作重试时,可以设计操作编号与服务器去重记录,先确定同一次操作的身份,而不是只增加一个重试循环。