Python 现代开发学习笔记

(十五)运行本地服务并发出请求

1. 在项目环境准备客户端与练习服务

进入配套的 network_project。目录中的 labserver 提供固定书目与错误响应,netreading 稍后完成并发导入,bookdata 继续使用第五章的记录和 SQLite 实现。

# 项目文件:network_project/pyproject.toml
# HTTPX 是运行依赖;pytest 继续放在开发组,src 下的包由构建工具发现。
[build-system]
requires = ["setuptools>=80,<83"]
build-backend = "setuptools.build_meta"

[project]
name = "reading-network-notes"
version = "0.1.0"
requires-python = ">=3.12,<3.13"
dependencies = ["httpx>=0.28,<1"]

[project.scripts]
reading-fetch = "netreading.cli:main"

[dependency-groups]
dev = ["pytest>=9.1,<10"]

[tool.setuptools.packages.find]
where = ["src"]

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --import-mode=importlib"
# 在 network_project 根目录使用 Python 3.12,同步配套的锁定记录。
uv sync --locked
# 手动查看服务时启动它,终端显示地址;Ctrl+C 结束。
uv run python -m labserver --port 8765

另一个终端或浏览器可以访问显示的地址。短例使用 with running_server() as server 临时启动相同服务,由系统选择空闲端口,结束后关闭,因此不用每次手动守着一个固定端口。

pip 路线在 Python 3.12 虚拟环境中执行 python -m pip install -e . 与 python -m pip install -r requirements-dev.txt,再用项目解释器运行相同代码。配套 HTTP 短例可在本目录执行 uv run python ../demos/http_get.py。

2. 先用标准库取得一条 JSON 记录

# 项目文件:demos/http_get.py
# 请求返回的正文先是 bytes;按响应声明的编码还原文本,再解释为 JSON。
import json
from urllib.request import Request, urlopen

from labserver import running_server


if __name__ == "__main__":
    with running_server() as server:
        request = Request(
            server.base_url + "/api/books/1", headers={"Accept": "application/json"}
        )
        with urlopen(request, timeout=1) as response:
            text = response.read().decode(
                response.headers.get_content_charset() or "utf-8"
            )
            record = json.loads(text)
            print(response.status, response.headers.get_content_type())
            print(record["title"], record["finished"])

输出 200 application/json 与 Python False。Request 保存目标、头部与可选正文;urlopen 执行阻塞请求,read 取得正文。这里把响应放进 with,读取后关闭资源,后续要重复请求时也清楚每次在哪里结束。urllib 请求接口

练习服务使用 ThreadingHTTPServer 接收本机请求,固定生成响应;/api/books/1?delay=0.1 会在发送响应前等待 0.1 秒。它让客户端可以观察等待重叠,服务器不会因为一个请求等待就停止接收其他请求。完整实现已分成书目、线程安全状态、路由与启动文件,见配套的 src/labserver/。标准库 HTTP 服务

客户端发出GET,服务等待并生成字节响应,客户端读取、解码和JSON解析

3. 查询参数与 JSON 请求体分别传入

# 项目文件:demos/http_query_post.py
# GET 的查询放在 URL,POST 的 JSON 放在请求体;服务器分别解析这两种位置。
import json
from urllib.parse import urlencode
from urllib.request import Request, urlopen

from labserver import running_server


if __name__ == "__main__":
    with running_server() as server:
        query = urlencode({"tag": ["基础", "语法"]}, doseq=True)
        with urlopen(
            server.base_url + "/api/echo?" + query + "#client-only", timeout=1
        ) as response:
            print(json.load(response)["query"])
        data = json.dumps({"minutes": 35}).encode("utf-8")
        request = Request(
            server.base_url + "/api/echo",
            data=data,
            method="POST",
            headers={"Content-Type": "application/json"},
        )
        with urlopen(request, timeout=1) as response:
            print(json.load(response))
        print("服务收到片段", any("#" in path for path in server.state.paths))

输出查询中的两个 tag、{'method': 'POST', 'body': {'minutes': 35}},最后为 服务收到片段 False。json.load 也能从这种提供 read 的二进制响应中解析 JSON,按 JSON 的字节编码规则处理正文。

笔记正文与全部题解 · Ctrl / ⌘ K