跳转至

第 4 周(9/28 一 – 10/4 日)+ 收官(10/5 – 10/7):Web 服务、架构与封装、综合项目

本周目标:HTTP/REST/OpenAPI 基础;FastAPI(三个工作日晚上各一块 + 假期深化):参数与 Pydantic、依赖注入、路由拆分、lifespan、异步端点、后台任务、SSE、测试;架构与封装(分层、Protocol 接口、依赖注入、错误边界);安全基础;在 Linux 服务器上运行。10/2 起进入综合项目 Notebook Service(规格见 plan/capstone.md)。

时长:周一–周三 4h × 3 + 国庆 10/1–10/4 10h × 4 = 52h;收官 10/5–10/7 10h × 3 = 30h。

版本基准(2026-09-05 核实):FastAPI 0.141.x(0.135.0 起内置 fastapi.sseEventSourceResponse)、Starlette 1.x、uvicorn 随 fastapi[standard] 安装;详见 research/11

对应 learn-agent:M17 → Week 4 的 FastAPI + SSE 接口;M18 → agent 的可测试架构(工具层 / 编排层 / 存储层);M19 → 部署到 H20 服务器。


周一 9/28(4h)|M17a HTTP、REST 与 FastAPI 入门

块 1(复习):第 3 周周测错题;聚合器遗留。

块 2(新知识): | 知识点 | 档 | | --- | --- | | HTTP 心智模型:请求(方法/路径/查询/头/体)→ 响应(状态码/头/体);GET 幂等、POST 创建、PUT/PATCH 更新、DELETE;常用状态码 200/201/204/400/401/403/404/409/422/500 | A | | JSON 作为传输格式;OpenAPI(Swagger)= 用 JSON Schema 描述接口;FastAPI 自动生成 /docs/openapi.json | A | | fastapi[standard] 安装了什么;fastapi dev app/main.py(开发热重载)vs uvicorn pkg.main:app | A | | 路径参数(类型自动转换与校验)、查询参数(默认值、Annotated[int, Query(ge=1)])、请求体(Pydantic 模型)、response_modelstatus_code=201 | A | | HTTPException(404, detail=...);Pydantic 校验失败自动 422 及其错误体结构 | A | | 用 TestClient(基于 httpx)写第一个 API 测试 | A |

块 3(练习)exercises/week4/w4_01_fastapi_basics/app.py 骨架 + test_app.py 已给 10 个测试):GET /healthGET /notes/{id}(404);GET /notes?tag=&limit=(查询校验);POST /notes(201 + response_model=NoteOut,输入 NoteIn 校验标题非空);DELETE /notes/{id}(204);内存字典存储。验收:测试全绿;打开 /docs 手动调一次。


周二 9/29(4h)|M17b 依赖注入、路由拆分、lifespan、设置

块 2(新知识): | 知识点 | 档 | | --- | --- | | Depends:把"拿数据库连接/当前设置/当前用户"变成可替换的函数;Annotated[Repo, Depends(get_repo)] 写法;yield 依赖做清理 | A | | APIRouter(prefix="/notes", tags=["notes"]) 拆文件;app.include_router | A | | lifespan 异步上下文:启动时建连接池/加载配置,关闭时释放(替代已弃用的 on_event);app.state | A | | 设置:pydantic-settings + @lru_cache def get_settings() 依赖 | A | | 自定义异常 → @app.exception_handler(NotFoundError) 映射为 HTTP 响应:领域层不 import FastAPI | A | | 测试:app.dependency_overrides[get_repo] = lambda: InMemoryRepo()TestClient(app) 作为上下文管理器触发 lifespan | A |

练习w4_02_fastapi_structure/:把周一的单文件拆成 main.py(创建 app、lifespan、异常处理器)、routers/notes.pydeps.pyget_settings/get_repo)、schemas.pyrepository.pyProtocol + 内存实现)、errors.py;测试用 dependency_overrides 注入内存仓储并验证 404 由领域异常映射而来。


周三 9/30(4h)|M17c 异步端点、后台任务、SSE、中间件、鉴权

块 2(新知识): | 知识点 | 档 | | --- | --- | | async def 端点 vs def 端点:同步端点在线程池里跑,异步端点里绝不能调阻塞函数(否则整个服务卡住);何时选哪个 | A | | BackgroundTasks:响应后继续做(发通知、写日志);长任务用队列(知道) | A | | SSEfrom fastapi.sse import EventSourceResponse, ServerSentEvent(0.135+);异步生成器逐条 yieldtext/event-stream;客户端 httpxstream 读取或 httpx-sse;与 StreamingResponse(任意分块)的区别;WebSocket 知道存在 | A | | 中间件:请求日志(耗时、状态码、请求 id);CORS 为什么不写 * | B | | 最小鉴权:Depends(verify_api_key)X-API-Key 头,对比 settings;OAuth2/JWT 知道存在 | B | | 异步测试:httpx.AsyncClient(transport=ASGITransport(app=app), base_url="http://test");测 SSE 用 stream 读前 N 条 | A |

练习w4_03_fastapi_async_sse/GET /events/countdown?n=5 SSE 每 0.05s 一条 ServerSentEvent(data=json, event="tick"),结束发 event="done"POST /importBackgroundTasks 模拟导入并把进度写入内存状态;GET /slow-sync vs GET /slow-asyncsleep(0.2) 后返回,测试并发 5 个请求两者耗时对比;请求日志中间件;X-API-Key 保护 /admin/stats;异步测试文件。

收尾:读 plan/capstone.md,画 Notebook Service 的模块图;预习 research/11 第四节项目结构。


周四 10/1(国庆,10h)|M18 架构与封装 + M19 安全与服务器

上午 3 块 — M18 架构与封装(A 档): - 分层routers(协议层,只做 HTTP ↔ 模型转换)→ services(用例/业务规则)→ repositories(存储,Protocol 接口 + SQLite/内存实现);schemas(Pydantic,传输模型)与 domain(dataclass,领域模型)分开,边界处映射。 - 依赖注入:构造函数传入依赖(NoteService(repo)),测试时传假实现;FastAPI 的 Depends 只是这件事的框架版。 - 错误边界:领域异常层次(NotFound/Conflict/Validation)在服务层抛,在协议层统一映射;不要让 HTTPException 渗入服务层。 - 纯核心 + 命令式外壳:计算不做 I/O,I/O 不做计算;这直接决定可测试性。 - Python 里真正常用的模式:策略 = 传函数;注册表 = 装饰器 + dict;工厂 = @classmethod;适配器 = 包一层实现 Protocol;观察者 = 回调列表;单例 = 模块级实例或 lru_cache。不要为了模式而模式。 - 《Cosmic Python》前 6 章的核心:领域模型、仓储、服务层、工作单元(知道)。 - 代码坏味道清单:函数 > 40 行、参数 > 5 个、布尔参数控制分支、dict 到处传、全局可变状态、try 包住 50 行。

下午前 2 块 — 练习 w4_04_refactor_kata/:给你一个 180 行、把 SQL、校验、格式化、print 全揉在一起的 legacy_notes.py 和一套针对纯函数与 Protocol 的测试 test_refactored.py(先全红)。目标:不改变行为地重构为 domain.py / repository.py / service.py / cli.py,测试全绿,legacy_notes.py 删除。这是 Notebook Service 的预演。

下午第 3 块 + 晚上 2 块 — M19 安全基础与服务器: | 知识点 | 档 | | --- | --- | | 输入校验全靠 Pydantic 与类型;绝不 eval/exec 用户输入;subprocess 不用 shell=True;路径拼接后 resolve() 并检查仍在允许目录内(路径穿越);pickle 只反序列化自己的数据;密钥只在环境变量;CORS 白名单;错误响应不泄漏堆栈;依赖锁定 uv.lockpip-audit/uv 审计(知道) | A | | Linux 服务器最小知识(H20):ssh user@host、SSH key、scp/rsync(或 git clone 到服务器);服务器上 curl -LsSf https://astral.sh/uv/install.sh \| shuv syncuv run uvicorn notebook.main:app --host 127.0.0.1 --port 8000tmux 保持进程;本机 ssh -L 8000:127.0.0.1:8000 user@host 后浏览器开 localhost:8000/docssystemd --user 与 Docker 知道存在 | B | | Linux 与 Windows 差异清单:路径分隔符(pathlib 已抹平)、大小写敏感、python3 命令、文件权限、换行符(Git autocrlf) | B |

练习:exercises/week4/w4_05_security_review.md——对照清单审查 kbaggregator,列出 ≥5 处改进并修掉 3 处;把 aggregator 在服务器上跑起来并用 ssh -L 访问 stub server(截图记进日志)。


周五 10/2 – 周日 10/4(国庆,30h)|综合项目 Notebook Service:里程碑 1–3

plan/capstone.md 的里程碑推进:M1 领域与仓储(10/2)、M2 服务与 API(10/3)、M3 导入/SSE/CLI(10/4)。每天结束:测试全绿、ruff/Pylance 零错误、提交推送、日志。周日晚 周测exercises/week4/quiz_week4.md)+ AI 评审。


收官 10/5 一 – 10/7 三(30h)

内容
10/5 里程碑 4:日志、配置、鉴权、错误边界收口;覆盖率 ≥80%;README;可选部署到 H20 并用 ssh -L 访问
10/6 里程碑 5:AI 评审(模板 4)→ 重构 ≤5 条 → 回归测试;uv build;打 tag v1.0.0;写复盘(做对了什么、卡在哪、下一步)
10/7 结业测验exercises/final/quiz_final.md,60 分钟闭卷:20 题 + 30 分钟限时编码);checklist.md 进阶 A 档三档复评;把"提/否"的条目排进 D+3/D+7/D+30;对照 learn-agent 的计划,标出哪些 Python 前置已经就位

本周阅读(详见 research/1113

模块 主读 补充
M17 FastAPI 官方教程(中文站):Path/Query/Body → Response Model → Handling Errors → Dependencies → Bigger Applications → Testing → SSE;Advanced:Lifespan、Middleware、Background Tasks research/11 第四节项目结构
M18 《Cosmic Python》第 1–6 章(免费在线);《Python 工匠》封装与解耦章 ArjanCodes 软件设计视频(仅补充)
M19 FastAPI Deployment 概念页;uv 在 Linux 安装文档 《Pro Git》服务器章节(可选)

本周陷阱清单(周测必考)

async def 端点里调 time.sleep/同步 SQLite/requests|服务层 import HTTPExceptionon_event 老写法|dependency_overrides 忘清理|response_model 漏掉导致泄漏内部字段|SSE 生成器里不 await 导致无法取消|CORS * + 凭证|API key 硬编码|路径拼接不检查穿越|服务器上用 0.0.0.0 暴露无鉴权接口|Windows 换行符污染脚本|uv run uvicorn 忘了 --reload 只在开发用