1. 本课定位是什么、为何重要前几课路由能跑、模型能校验但项目稍大就会重复每个接口自己读环境变量、自己解析分页、很快自己开数据库会话、再往后自己取当前用户。复制粘贴不仅丑更会让默认值不一致——有的接口 page 从 0 起、有的从 1 起联调灾难。本课两件事用 FastAPI 的Depends声明「路由需要什么前置结果」用pydantic-settings把配置集中到 Settings从环境变量 /.env读取密钥不进源码仓库。目录开始靠拢app/core、app/api。数据仍可不落库但为第 44 课get_db预留同一套注入习惯。贯穿业务仍是书签 API 的配置与分页骨架。概念一句话依赖注入路由声明「我需要 X」框架先算好 X 再调用你Settings集中管理的配置对象常用 pydantic-settingsAnnotated 别名把Depends(...)收成类型别名路由签名更短分层目录api/core/schemas各司其职为何重要密钥进 Git、到处os.getenv、分页逻辑复制 N 份——是项目腐化加速器。对比已学已学本课路由函数自己解析全部参数公共逻辑抽到 Depends字符串常量当配置.env Settings单文件 / 浅目录 demo可增长的包结构只关心业务 JSON关心配置与复用2. 本质调用路由前先跑依赖依赖注入名字吓人入门可以记成函数参数上写Depends(fn)框架保证先执行fn及其子依赖再把返回值塞进参数。分页、配置、下节的 Session、再下节的当前用户都是同一套路。上一课路由函数直接收 Body这一课路由函数开始「声明需求」。先看最小分页依赖再上 Settings最后拼目录顺序从具体到结构。本质Depends(fn)≈ 调用路由前先执行fn结果注入参数。defget_pagination(page:int1,size:int10):return{page:page,size:size,offset:(page-1)*size}router.get(/items)deflist_items(pDepends(get_pagination)):...请求进入 - 解析 Query/Path/Body - 执行依赖树get_settings / get_pagination / ... - 调用路由函数 - 返回响应3. 约束与常见坑配置与依赖一旦用错问题常在「另一台机器上才爆」密钥写死被推仓库、从错误工作目录启动读不到.env、Settings 缓存导致改 env 不生效。数据库依赖若开了不关会在第 44 课变成连接泄漏——本课先立规矩。坑表请认真过一遍综合实践里改 APP_NAME 验证「配置真的外置」。约束密钥、数据库 URL 不进仓库提交.env.example即可。Settings 建议进程内单例lru_cache测试替换时注意清缓存。.env相对启动时的当前工作目录。依赖应尽量纯、可测避免偷偷写重业务副作用。子依赖可以再 Depends形成依赖树但不要环。常见坑坑现象正确直觉SECRET_KEY123写源码并 push令牌可被伪造环境变量 长随机串每个路由os.getenv默认值不一致统一 Settings依赖里开了 DB 却不关连接泄漏用yield第 44 课从错误目录启动读不到.env固定项目根启动改.env不生效仍旧配置重启进程或清get_settings缓存把.env提交 Git秘密扩散gitignore只交 example4. 配置项按用途归组配置不是「有什么写什么」而是按用途归组方便以后加项时知道放哪。本课示例包含应用名、调试开关、数据库 URL 占位、密钥占位——后两项给 44/45 课铺路本课路由可以只读前几项。好坏对照表帮你建立代码审查直觉看到源码里的长密钥字符串就应报警。组示例键用途应用APP_NAME,DEBUG标题、是否暴露详细错误数据DATABASE_URL连接串第 44 课用安全SECRET_KEYJWT 等第 45 课坏好源码写死密钥Settings.secret_key从环境读每人一套魔法路径DATABASE_URL可配置无示例文件.env.example列出键名真密钥进 exampleexample 只用占位符字段名映射直觉环境变量APP_NAME常对应 Settings 字段app_namepydantic-settings 大小写不敏感匹配习惯。5. Settings 最小写法BaseSettingsSettingsConfigDict(env_file.env)是当代常见写法。lru_cache包一层get_settings既当依赖入口又避免每次请求重新读盘解析。小步代码先认形状综合实践再放进包路径app.core.config。fromfunctoolsimportlru_cachefrompydantic_settingsimportBaseSettings,SettingsConfigDictclassSettings(BaseSettings):model_configSettingsConfigDict(env_file.env,extraignore)app_name:strBookmark APIdebug:boolTruedatabase_url:strsqlite:///./bookmarks.dbsecret_key:strchange-melru_cachedefget_settings()-Settings:returnSettings()点说明extraignore.env多出来的键不报错默认值无环境变量时的开发默认lru_cache进程内单例6. Depends 与 Annotated 别名路由签名里反复写settings: Settings Depends(get_settings)又长又吵。Python 3.9 常用Annotated[Settings, Depends(get_settings)]收成SettingsDep路由只写settings: SettingsDep。这是风格选择但团队统一后可读性明显上升。分页同理做成PaginationDep。fromtypingimportAnnotatedfromfastapiimportDepends,Querydefget_pagination(page:intQuery(1,ge1),size:intQuery(10,ge1,le100),)-dict:return{page:page,size:size,offset:(page-1)*size}SettingsDepAnnotated[Settings,Depends(get_settings)]PaginationDepAnnotated[dict,Depends(get_pagination)]修改前修改后每个列表接口复制 page/size 校验统一PaginationDepoffset 有的写错成 page*size一处计算处处正确路由里os.getenv(APP_NAME)settings.app_nameoffset 公式(page - 1) * sizepage 从 1 起。7. 目录骨架本课起靠拢从本课开始示例路径从「根目录 main.py」升级为app包。这不是为了炫技而是为 44 课模型、45 课鉴权留抽屉。导入写成from app.core.config import get_settings启动写成uvicorn app.main:app。若启动报模块找不到优先检查是否在项目根执行、是否有各层__init__.py。day43/ .env.example .env # 本地不提交 app/ __init__.py main.py core/ __init__.py config.py api/ __init__.py deps.py routers/ __init__.py meta.py目录职责core配置、安全、DB 引擎api路由与 DependsschemasPydantic本课可暂空稍后modelsORM第 44 课8. 落地场景书签项目的配置与分页本课演示接口可以偏「元信息」首页返回 app 名/demo/pages演示分页切片。书签资源路由可暂不写满 CRUD重点是依赖与配置习惯正确——第 44 课会把 Session 依赖接进 bookmarks 路由。把场景想成同一套 Depends明天换列表数据源路由签名几乎不动。场景依赖首页展示应用名Depends(get_settings)/SettingsDep列表分页PaginationDep下课接库Depends(get_db)同构再下节鉴权Depends(get_current_user)同构9. 子依赖直觉了解依赖可以嵌套get_db可能 Dependsget_settings读 URLget_current_user可能 Depends 安全方案。框架会按树解析同一请求内可缓存同依赖进阶细节不必死记。入门只要知道你可以把小依赖组合成大依赖路由仍然只声明最外层需要的那个。list_bookmarks └─ get_db └─ get_settings # 读 database_url10. 环境准备新增依赖pydantic-settings。Windows 推荐 Cygwin/WSL 执行 heredoc务必在包含.env的项目根启动 uvicorn。mkdir-p~/python-lab/src/day43/app/core ~/python-lab/src/day43/app/api/routerscd~/python-lab/src/day43 pipinstallfastapi0.110uvicorn[standard]0.27pydantic-settings2.011. 综合实践完整可运行脚本一次创建.env.example、.env、config、deps、router、main 与各__init__.py。验证/读到 Day43 Lab验证分页第二页切片。然后改.env的 APP_NAME 并重启看配置是否变化。启动命令uvicorn app.main:app --reload --port 8000在 day43 根目录。mkdir-p~/python-lab/src/day43/app/core ~/python-lab/src/day43/app/api/routerscd~/python-lab/src/day43cat.env.exampleEOF APP_NAMEBookmark API DEBUGtrue DATABASE_URLsqlite:///./bookmarks.db SECRET_KEYchange-me EOFcat.envEOF APP_NAMEDay43 Lab DEBUGtrue SECRET_KEYdev-only-secret EOFcatapp/__init__.pyEOF EOF cat app/core/__init__.py EOF EOFcatapp/api/__init__.pyEOF EOF cat app/api/routers/__init__.py EOF EOFcatapp/core/config.pyEOF from functools import lru_cache from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config SettingsConfigDict(env_file.env, extraignore) app_name: str Bookmark API debug: bool True database_url: str sqlite:///./bookmarks.db secret_key: str change-me lru_cache def get_settings() - Settings: return Settings() EOFcatapp/api/deps.pyEOF from typing import Annotated from fastapi import Depends, Query from app.core.config import Settings, get_settings def get_pagination( page: int Query(1, ge1), size: int Query(10, ge1, le100), ) - dict: return {page: page, size: size, offset: (page - 1) * size} SettingsDep Annotated[Settings, Depends(get_settings)] PaginationDep Annotated[dict, Depends(get_pagination)] EOFcatapp/api/routers/meta.pyEOF from fastapi import APIRouter from app.api.deps import PaginationDep, SettingsDep router APIRouter(tags[meta]) router.get(/) def root(settings: SettingsDep): return {app: settings.app_name, debug: settings.debug} router.get(/demo/pages) def demo_pages(p: PaginationDep): all_items [fitem-{i} for i in range(1, 31)] chunk all_items[p[offset] : p[offset] p[size]] return {page: p[page], size: p[size], items: chunk} EOFcatapp/main.pyEOF from fastapi import FastAPI from app.api.routers import meta from app.core.config import get_settings settings get_settings() app FastAPI(titlesettings.app_name, debugsettings.debug) app.include_router(meta.router) EOFuvicorn app.main:app--reload--host127.0.0.1--port8000验证curl-shttp://127.0.0.1:8000/curl-shttp://127.0.0.1:8000/demo/pages?page2size5预期{app:Day43 Lab,debug:true}第二页 items 约为item-6…item-10。检查标准应用名来自.env的 Day43 Labpage2 size5offset5五项 item-6…10size0422ge1改 APP_NAME 后重启/显示新名对照改.env后需重启或清get_settings缓存再看/。12. 生命周期扫盲铺垫FastAPI 支持启动/关闭事件或 lifespan适合建连接池、释放资源。本课不强制实现但要知道配置在 import 时读取、重资源更适合放在 lifespan。第 44 课的create_all可能放在启动阶段。先有印象避免以后把「连数据库」散落在每个路由第一次访问时的临时逻辑里。时机适合做import 时读 Settings、创建 app启动 lifespan建表检查、连接池预热请求依赖Session、当前用户关闭 lifespan释放连接池13. 常见问答Q.env和系统环境变量谁优先A以 pydantic-settings 版本与配置为准实践中避免两套互相打架开发用.env即可。Q为什么 example 里也有 SECRET_KEYA只占位提醒「需要这个键」真实值放本地.env永不提交。QDepends 会拖慢每个请求吗A轻量依赖开销通常可忽略重逻辑才需要缓存/优化。Q能否在依赖里 raise HTTPExceptionA可以常用于鉴权失败 401第 45 课。14. 自我检查清单能解释 Depends 在路由前执行会写 Settings get_settings会.env/.env.example分工会分页依赖与 offset 计算会 Annotated 别名会uvicorn app.main:app从项目根启动知道密钥不进 Git15. 与前后课衔接课关系42模型合同稳 → 本课抽公共前置44同构注入get_db CRUD 落盘45同构注入get_current_user总结带走Depends 复用前置逻辑配置外置目录分层从现在开始分页 offset 一处算对。依赖注入管理变化点配置、连接、用户让路由只剩业务叙事。Depends在调用路由前注入公共结果。Settings .env管理配置密钥不进仓库。.env.example给协作者.env仅本地。分页统一依赖offset (page - 1) * size。Annotated别名让路由签名更短更稳。包结构app/core、app/api为接库与鉴权留抽屉。小练笔先做再看答案。可选实践改 APP_NAME 验证重启后变化。题 1为什么不要把SECRET_KEY提交到 Git题 2Depends(get_settings)的好处题 3.env与.env.example分别给谁题 4分页offset如何由page与size计算题 5判断依赖函数也可以再 Depends 其它依赖。题 6page3, size10时 offset 是多少题 7从错误目录启动 uvicorn最常见的配置问题是题 8lru_cache包在get_settings上主要为了题 9可选实践把.env的APP_NAME改成Bookmark Dev重启后 curl/验证。题 10一句话说明路由为什么不该到处os.getenv。小练笔参考答案题 1泄露后可伪造令牌等安全凭证。题 2统一配置入口便于测试替换避免散落 getenv。题 3.env本地真实配置不提交.env.example给协作者看键名。题 4(page - 1) * size题 5对子依赖。题 620题 7读不到预期的.env工作目录不对。题 8进程内单例避免重复构建 Settings。题 9响应中 app 字段应为Bookmark Dev以你修改为准。题 10默认值易不一致、难测、密钥易散落应集中 Settings。合理即可