惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
U
Unit 42
H
Help Net Security
博客园_首页
雷峰网
雷峰网
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园 - 【当耐特】
Recent Announcements
Recent Announcements
Recorded Future
Recorded Future
aimingoo的专栏
aimingoo的专栏
爱范儿
爱范儿
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Cloudflare Blog
S
Security @ Cisco Blogs
M
MIT News - Artificial intelligence
Cyberwarzone
Cyberwarzone
Cisco Talos Blog
Cisco Talos Blog
Spread Privacy
Spread Privacy
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
K
Kaspersky official blog
MyScale Blog
MyScale Blog
阮一峰的网络日志
阮一峰的网络日志
C
CERT Recently Published Vulnerability Notes
V
Vulnerabilities – Threatpost
WordPress大学
WordPress大学
C
Cisco Blogs
G
Google Developers Blog
N
News and Events Feed by Topic
P
Palo Alto Networks Blog
Apple Machine Learning Research
Apple Machine Learning Research
Schneier on Security
Schneier on Security
博客园 - 聂微东
Security Latest
Security Latest
Security Archives - TechRepublic
Security Archives - TechRepublic
O
OpenAI News
云风的 BLOG
云风的 BLOG
IT之家
IT之家
PCI Perspectives
PCI Perspectives
Microsoft Security Blog
Microsoft Security Blog
NISL@THU
NISL@THU
小众软件
小众软件
Scott Helme
Scott Helme
大猫的无限游戏
大猫的无限游戏
L
LINUX DO - 最新话题
Microsoft Azure Blog
Microsoft Azure Blog
Simon Willison's Weblog
Simon Willison's Weblog
N
News and Events Feed by Topic
F
Fortinet All Blogs
The Last Watchdog
The Last Watchdog

博客园 - 一名程序媛呀

Anki插件开发必知必会:钩子函数与右键菜单定制 httpx 传参总报错?这次把 GET、POST、文件上传到响应处理的坑给你一次填平 从卡顿到丝滑:FastAPI 调用外部 API 的正确姿势(httpx 实战) 还在 XHR、Fetch 和 Axios 之间纠结?我踩过的坑,希望你一个都不用碰到 你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南 Uvicorn、Gunicorn 傻傻分不清?FastAPI 生产部署避坑指南 Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招! 刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API 别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了 掏出手机就能搭个 WebDAV 同步服务器?这操作有点香 别只盯着GitBook了!这个文档神器让你的笔记秒变网站 写爬虫时用了代理还被封?Python 代理的那些隐藏坑,我替你踩明白了 FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好 旧手机别扔!用 Termux 搭个私人云盘,比网盘香多了 你的FastAPI又在服务器上“跑不起来”了?来,今天咱把打包这件事彻底聊透 写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了 前端包管理咋选?我从npm叛逃到pnpm的血泪史(附避坑指南) 聊聊 fetch 使用中我踩过的那些坑和正确打开方式 FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示? 给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍 我用了FastApiAdmin后,连夜把踩过的坑都整理出来了 告别 Typora 后的新欢:我把所有笔记迁移到了 Obsidian 这个“第二大脑” 你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线 让 FastAPI Agent 思考不阻塞:手把手教你实现异步任务与后台处理方案 让FastAPI Agent真正记住你:聊聊会话记忆与持久化存储的落地实践 FastAPI Agent 函数调用实战:我让 AI 学会了“自己动手查天气“ 初探:用 FastAPI 搭建你的第一个 AI Agent 接口 FastAPI 少有人提的实用技巧:把 Depends 依赖提到路由层,代码少写60% FastAPI 生产环境静态文件完全指南:从 /favicon.ico 404 到 HSTS 混合内容,一次全根治 用了loguru我才明白,Python日志还能这么写 FastAPI 后台任务:BackgroundTasks 的使用场景与注意事项 FastAPI 文件上传避坑全指南:分块存盘、类型校验与安全兜底 FastAPI + Pydantic 模型终极实战手册:从能跑就行到固若金汤,这些技巧你一定用得上 FastAPI + SQLAlchemy 2.0 通用CRUD操作手册 —— 从同步到异步,一次讲透 FastAPI订单防超卖实战:从数据库锁到Saga分布式事务,这一篇给你理清了 FastAPI 生产环境避坑指南:用 Alembic 管理数据库迁移,别再手动改表结构了! FastAPI服务半夜又挂了?先别急着重启,查查你的数据库连接池“池子”是不是漏了 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 Vue 3 组合式 API 香是香,但从Vue2迁移时你可别像我当初一样踩进这 3 个深坑里 我用fastapi-scaff搭了个项目,两天工期缩到两小时,老板以为我开挂了 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! FastAPI自动生成的API文档太丑?我花了一晚上把它改成了客户愿意付费的样子 告别手写 API 胶水代码:FastAPI 与 Vue 的“契约自动机” OpenAPI 实战 FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南” FastAPI + Celery 实战:异步任务里调用 Redis 和数据库的全解析,及生产级组织方案 FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了! FastAPI + Celery 实战:异步任务的坑与解法,我帮你踩了一遍 FastAPI子应用挂载:别再让root_path坑你一夜 FastAPI项目半夜报警吵醒你?聊聊告警这事儿怎么搞! 别再数据线了!用FastAPI 5分钟搭个局域网文件+剪贴板神器 FastAPI单元测试实战:别等上线被喷才后悔,TestClient用对了真香! FastAPI状态共享秘籍:别再让中间件、依赖和路由“各自为政”了! FastAPI实战:WebSocket vs Socket.IO,这回真给我整明白了! 从0到1,FastAPI + PostgreSQL + Tortoise ORM 实战避坑指南 FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀 FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透
FastAPI配置管理避坑指南:从硬编码到 .env 与 pydantic_settings 类,连路由用法都给你捋清楚
一名程序媛呀 · 2026-04-28 · via 博客园 - 一名程序媛呀

你是不是也干过这种事:写完接口,顺手把数据库地址、密码或者 API_KEY 往代码里一写,然后高高兴兴地 git push

千万别这样偷懒。之前看到过把自己生产环境的 Redis 密码直接写在 settings.py 里,然后传到了公开仓库……结果第二天:整个库被清空,留下一行“请支付比特币”

想想就后背发凉。也从那以后我就彻底和硬编码说再见了。今天咱们就来聊聊怎么把 FastAPI 的配置管理从“裸奔”升级到“装甲车”。

🎯 本文能帮你解决什么

✔️ 敏感信息泄漏风险,再也不会把密钥传到 GitHub
✔️ 快速切换开发/测试/生产环境,改一个变量就行
✔️ 用 pydantic_settings 自动校验配置,少写一堆 try-except

📌 主要内容脉络

咱们先从一个痛点说起,然后一步步把硬编码改成环境变量,再升级到 pydantic_settings 的 BaseSettings ,顺便说清 dotenv pydantic_settings 到底怎么选。最后还会聊多环境配置覆盖和优先级那点事儿。

🚨 第一部分:硬编码的痛,你可能正在经历

很多项目一开始为了“快”,直接把数据库连接信息写成字符串。像这样:

DATABASE_URL = "postgresql://user:pass123@localhost/db"

看起来没毛病,直到你需要部署到服务器、要换一套数据库地址。

要么你手动改代码打包,要么写一堆 if-else 判断环境。更可怕的是,万一代码仓库公开,密码就等于贴在了公告栏上

你可能会问:“那我加到 .gitignore 里不就行了?” 是的,但配置本身还是散落在各处,团队一多照样乱套。

🔧 第二部分:从环境变量到 Settings 类,步步为营

好,咱们先来第一步——把秘密藏进环境变量。

在终端 export 或者在系统里设置一个 DATABASE_URL ,然后代码里用 os.getenv 去取。这样至少不会把密码写在代码里了。

但是你很快会发现,每次重启机器环境变量就丢了,而且得挨个给队友配一遍,极其麻烦。这时候就该 .env 文件 出场了。

接下来重点来了——怎么把 .env 用起来?到底是选 python-dotenv 还是 pydantic_settings

这个工具的选择,好比选螺丝刀,不是最贵的就好,而是看你的场景。
如果你只是想要一个简单的 key-value 读取,dotenv 足够轻量
但一旦你需要类型校验、字段默认值、多环境自动切换,pydantic_settings 的 BaseSettings 绝对是更聪明的选择

再说个容易翻车的点:
.env 文件一定要立刻加到 .gitignore 里,并且提供一个 .env.example 模板文件给队友。我有次就是忘了这一步,差点把密钥推上去。

👩‍💻 第三部分:实战 —— 用 BaseSettings 管好你的配置

直接上代码,一看就懂:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "MyFastAPI"
    database_url: str
    secret_key: str
    debug: bool = False    

    class Config:
        env_file = ".env"
        env_file_encoding = "utf-8"

settings = Settings()

然后你的 .env 文件长这样:

APP_NAME=FastAPI配置管理测试项目
DATABASE_URL=postgresql://user:pass@localhost/db
SECRET_KEY=super-secret-key
DEBUG=false

你注意到没, 字段名大小写自动匹配,而且 debug 直接当布尔值用,不用自己 int("0") 转换。
pydantic 还会帮你做校验,万一环境变量里没设 database_url ,应用启动时直接抛错,避免带着空配置跑起来。

是不是以为这样就完了?还不够。生产环境的配置通常会覆盖某些默认值,这时你可以这么做:

class Settings(BaseSettings):
    app_name: str = "MyApp"
    debug: bool = False

    class Config:
        env_file = ".env"
        # 当存在 .env.prod 时优先加载
        env_file = ".env.prod" if os.getenv("ENV") == "prod" else ".env"

官方文档虽然没强制,但根据我的经验,最好把不同环境的 .env 文件分开,再用环境变量 ENV 来决定加载哪个。

📂 .env 文件放哪里?不是无所谓,但也很灵活

既然 Settings 类已经在用了,那 .env 文件到底该扔在哪儿,绝对是个“说大不大,说小坑一堆”的问题。好,咱们把它掰扯清楚。

默认情况下,pydantic_settings 会在当前工作目录(CWD)下找你的 .env 文件。也就是说,你在哪个目录执行的 Python 命令,它就去哪个目录找。

举个例子,如果你的项目结构长这样:

 my_project/ 
 ├── app/ 
 │ ├── main.py 
 │ └── config.py 
 ├── .env 
 └── requirements.txt 

你在 my_project 目录下执行 python app/main.py 或者 uvicorn app.main:app ,当前工作目录就是 my_project,所以它能找到同级的 .env,稳稳的。

但是!一旦你换个姿势,比如在别的地方执行,或者用 Docker、systemd 启动,工作目录往往就跑偏了。
我也曾经因为这个问题在服务器上排查了一下午,配置死活不生效,最后发现是 supervisor 把工作目录切到了 /root…… 😭

🎯 最佳实践:显式指定路径,不依赖 CWD

别再猜来猜去了,直接在 Settings 类的 Config 里把路径写清楚。这里有一个我屡试不爽的写法:

from pydantic_settings import BaseSettings
import os 

class Settings(BaseSettings):
    database_url: str
    secret_key: str

    class Config:
        # 关键!用 file 定位到 config.py 同级目录下的 .env
        env_file = os.path.join(os.path.dirname(__file__), ".env")
        env_file_encoding = "utf-8"

这么一来,不管你在哪里执行,它永远去找 config.py 所在目录下的 .env 文件
也就是说,把 .env config.py 放在同一个文件夹里(通常是 app 目录下),就再也不会丢了。

你可能会问:“那多环境怎么办?比如 .env.prod 放哪儿?” 同样啊,也放 config.py 旁边,然后根据环境变量稍微改一下路径就行:

import os

env = os.getenv("APP_ENV", "dev")
env_file = os.path.join(os.path.dirname(__file__), f".env.{env}") 

🧪 查找机制的底层逻辑(一句话版)

如果你不显式指定路径,pydantic_settings 内部其实调用的是 python-dotenv 的 load_dotenv(env_file) ,它就会基于当前工作目录去解析相对路径。所以这玩意儿就像“前台的快递”,你得站在正确的工位上才能收到。
而用 __file__ 的方式则相当于你把收货地址写成了“我家”,无论快递员从哪个邮局出发,都能送到。

📣 几个血的教训

✖️ 不要放在项目的上级目录,否则部署时很容易漏掉。
✖️ 不要放在会随代码一起被静态文件服务暴露的目录下(比如 static/),别人直接通过 URL 就可能下载到。
✔️ 一律放在项目代码包内部,和 config.py 做邻居,再配合 .gitignore,最安全。

🧠 第四部分:配置优先级 —— 别被覆盖规则搞晕

这里有个很容易踩的坑。 pydantic_settings 加载配置的优先级是这样的:

🥇 系统环境变量(最高优先级)

🥈 .env 文件中的值

🥉 你在类里设置的默认值

也就是说,哪怕 .env 里有 DATABASE_URL ,如果你在启动容器时传入了同名环境变量,它会被覆盖
这其实是个好事,特别适合在 k8s docker-compose 里注入密钥而不改动镜像。

🍳 在路由里用上你的 Settings

直接上最简单粗暴的办法——导入实例,当普通对象用

假设你的配置类写在 config.py 里,并且已经实例化成了 settings,然后在路由文件里:

from fastapi import FastAPI
from config import settings

app = FastAPI()

@app.get("/info")
async def get_app_info():
    return {
        "app_name": settings.app_name,
        # ⚠️ 千万别把 secret_key 等敏感信息直接返回!这里只是演示取值
        "database_connected": bool(settings.database_url)
    }

你可能会问:“这跟直接用全局变量有啥区别?”那区别可就大了——
这里所有敏感值都来自外部,代码里没硬编码,而且 pydantic 在启动时就会校验类型,数据库地址格式不对的话,应用压根起不来,早报错早治疗

💡 进阶玩法:用依赖注入,测试更友好

上面那种直接导入的方式虽然快,但写单元测试时不好替换配置。更优雅的做法是把 settings 变成一个 FastAPI 依赖,就像这样:

from fastapi import Depends

def get_settings():
    return settings

@app.get("/db-status")
async def db_status(config: Settings = Depends(get_settings)):
    # 现在你的路由函数可以拿到 config 对象,测试时也能覆盖
    return {"db_url_prefix": config.database_url.split(":")[0]}

这里有个容易翻车的点:千万别在 Depends 里直接返回一个普通 dict,那样你会丢掉 pydantic 的校验和 IDE 自动补全。坚持用 Settings 类的实例,开发体验能好一大截。

🔐 一个特殊的常见需求:在路由里用密钥

有时候需要在视图里签发 JWT 或者调用第三方 API,自然就得用到 secret_key 或 api_token。
这时建议 把 settings 实例注入到工具函数里,而不是直接在路由里裸用字符串

from jose import jwt

def create_token(data: dict, settings: Settings):
    return jwt.encode(data, settings.secret_key, algorithm="HS256")

@app.post("/login")
async def login(config: Settings = Depends(get_settings)):
    token = create_token({"user": "test"}, config)
    return {"access_token": token}

最后啰嗦一句:无论哪种方式,.env 文件本身必须待在 .gitignore 里
我见过太多次“配置类写得完美,结果把 .env 顺手 push 上去”的惨案,每次都得连夜轮换密钥 😭。
还有就是千万别在生产环境开启 debug=true ,不然报错时所有堆栈信息全会返回给前端,又是一场事故。

💬 总结与互动

通过以上这些操作,你的 FastAPI 路由就能安全又优雅地用上配置了。从硬编码到环境变量,再到用 BaseSettings 优雅地管理多环境配置,这条路我走得跌跌撞撞,但真的投入产出比超高。
现在你新起一个 FastAPI 项目,只需要花上 10 分钟把 Settings 类搭好,后面就再也不用为“密码怎么传”这种事头疼了。

你有过配置泄露的惊险时刻吗?或者你在用别的配置管理小技巧?来评论区唠唠,咱们一起避坑 😭。或者还有什么想聊的?评论区等你,一起填坑 👷‍♀️


觉得有用就点个「赞」+ 「关注」,省得下次想找时迷路。
也欢迎转发给那个还在硬编码密码的同事,救一个是一个 👊