FastAPI 依赖注入被你用成了全局变量?别慌,三分钟讲透
一、你可能正在这样写
先来做个自我检测,看看你的代码有没有“中招”。打开你的 FastAPI 项目,如果发现了下面任何一种写法,那么恭喜——你可能已经成功地把依赖注入用成了全局变量。
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
写法一:在依赖函数里塞了个列表,全项目共享状态
from fastapi import FastAPI, Depends
app = FastAPI()
# 这玩意儿叫“全局变量”,不叫“依赖注入”
cache = []
@app.get("/items")
async def get_items():
cache.append("data")
return {"cache": cache}
写法二:在路由函数里直接 import 一个单例,假装在用依赖注入
from fastapi import FastAPI, Depends
app = FastAPI()
class Database:
def __init__(self):
self.connection = "I'm a global db!"
db = Database() # 这行代码,值得警惕
@app.get("/items")
async def get_items(database: Database = Depends(lambda: db)):
return database.connection
写法三:在依赖函数里保存了上一次请求的状态
from fastapi import FastAPI, Depends
app = FastAPI()
def get_user():
# 每次请求,上一个用户的数据还留着
user_data = {"name": "last_user"}
return user_data
@app.get("/profile")
async def profile(user = Depends(get_user)):
return user
如果以上写法你都没用,那真要恭喜你:要么你是天赋异禀,要么就是项目还没真正开始。如果中了招——别慌,往下看,三分钟带你回到正轨。
二、依赖注入到底是什么
在讨论“什么错了”之前,得先搞清楚“什么是对的”。
FastAPI 的依赖注入,本质上是一个请求级别的工厂函数。每次请求到来,框架都会帮你调用一次这个“工厂”,把返回值注入到路由函数里。请求一结束,这个对象也就随之消失了。
打个比方,就像医院挂号:你去医院看病,医生让你去抽血。抽血窗口每次只服务一个病人,你抽完走了,下一个人来,护士会重新准备一套全新的工具。
依赖注入就是这个逻辑——每个请求拥有自己独立的对象,彼此互不干扰。
而全局变量则是另一回事:你抽完血走了,抽血窗口的工具还留在那儿,下一个人来了,发现里面可能还躺着上一个人的血样。
这就是 FastAPI 依赖注入的核心特征:请求隔离,自动清理。

三、正确的打开方式
场景一:数据库连接(最常见)
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
# 每次请求进来,创建新的连接;请求结束,自动关闭
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users/{user_id}")
async def get_user(user_id: int, db: Session = Depends(get_db)):
return db.query(User).filter(User.id == user_id).first()
关键点:这里的 yield 是精髓——yield 前面的代码在请求开始时执行,yield 后面的代码在请求结束时执行,资源清理自动完成,干净利落。
场景二:用户认证(链式依赖)
from fastapi import Depends, HTTPException
# 第一层:提取 Token
def get_token(token: str = Header(None)):
if not token:
raise HTTPException(status_code=401, detail="未登录")
return token
# 第二层:验证 Token,返回用户信息
def get_current_user(token: str = Depends(get_token)):
user = verify_jwt(token)
if not user:
raise HTTPException(status_code=401, detail="Token 无效")
return user
# 在路由中使用,依赖链自动解析
@app.get("/profile")
async def profile(user = Depends(get_current_user)):
return {"username": user.username}
关键点:get_current_user 依赖 get_token,FastAPI 会自动帮你处理调用顺序,路由函数里只需要写 Depends(get_current_user) 即可,逻辑清晰,职责分明。
场景三:应用级全局依赖(跨所有路由)
from fastapi import FastAPI, Depends, Header, HTTPException
async def verify_api_key(x_api_key: str = Header(...)):
if x_api_key != "your-secret-key":
raise HTTPException(status_code=403, detail="API Key 不对")
return x_api_key
# 加在 app 上,所有路由都要过这关
app = FastAPI(dependencies=[Depends(verify_api_key)])
@app.get("/items")
async def read_items():
return [{"item": "Portal Gun"}]
@app.get("/users")
async def read_users():
return [{"username": "Rick"}]
关键点:app = FastAPI(dependencies=[...]) 是全局拦截器,所有路由都要过这一关。这非常适合添加像 API Key 校验这种“每个接口都必须有”的统一逻辑。
四、什么时候真的可以用“全局变量”
坦白说,有两种情况,使用全局变量是合理且安全的:
情况一:配置参数(不涉及请求状态)
from fastapi import FastAPI
app = FastAPI()
# 写死的配置常量,不是请求状态,完全 OK
MAX_PAGE_SIZE = 100
DEFAULT_TIMEOUT = 30
情况二:Lru_cache 缓存(读写分离,明确知道自己在干什么)
from functools import lru_cache
@lru_cache()
def get_config():
# 应用启动时加载一次,整个进程生命周期内不变
return load_config_from_file()
@lru_cache()
def get_redis_client():
# 连接池,通常在应用启动时建立,不随请求创建/销毁
return redis.Redis(host="localhost")
记住一个简单的原则:但凡涉及请求状态(用户数据、token、请求参数),就老老实实用 Depends。配置和只读的缓存,才是全局变量的主场。
五、避坑清单
来做个快速自检,看看你的项目里有没有这些“坑”:
- 数据库连接、Redis 连接用了
global关键字?赶紧删掉。 - 依赖函数里存了 list 或 dict 来“暂存”数据?马上换掉。
- 出现了
Depends(lambda: db_instance)这种写法?这说明你在用全局变量糊弄依赖注入系统,别骗自己。 - 每次请求需要独立的用户上下文?用链式
Depends来解决。 - 想给所有路由统一加校验?用
app = FastAPI(dependencies=[...])这个全局依赖。
六、总结
依赖注入不是什么高深莫测的概念,它就做一件事:让每个请求有自己独立的对象,用完自动清理,不用开发者操心。
把它用成全局变量,不是因为你笨,往往是因为 FastAPI 写起来太顺手了——顺手到让人忍不住想走捷径。
但捷径是有代价的:用户 A 的数据莫名其妙跑到用户 B 的响应里,这种诡异又难查的 Bug,足以让人怀疑人生。
所以,请牢记这个核心原则:依赖注入管的是“请求里的东西”,配置常量才是真正的“全局东西”。分清楚这个,你就能避开绝大多数坑。
相关攻略
为什么需要持久化? 今天我们来深入聊聊APScheduler的两个进阶配置:任务持久化和分布式锁。这两个配置,可以说是让你的定时任务从“能跑”的玩具,升级为“生产可用”的可靠工具的关键一步。 你是否有过这样的经历?用APScheduler写了个定时任务,跑得好好的,结果服务一重启,所有任务都消失了。
你以为 FastAPI 封装得够好了,随便写两句就能跑?天真 做后端开发,文件上传和下载是绕不过去的坎。FastAPI 的封装确实优雅,但如果你以为随便写两句就能高枕无忧,那可就太天真了。今天,我们就来盘点三个在实际项目中高频踩坑的场景,看看你中招了几条。 坑一:大文件上传,内存原地爆炸 很多新手第
一、你可能正在这样写 先来做个自我检测,看看你的代码有没有“中招”。打开你的 FastAPI 项目,如果发现了下面任何一种写法,那么恭喜——你可能已经成功地把依赖注入用成了全局变量。 写法一:在依赖函数里塞了个列表,全项目共享状态 from fastapi import FastAPI, Depen
FastAPI 中间件深度解析:从原理到实战应用 简单来说,中间件充当了HTTP请求处理流程中的统一拦截层。所有进入应用的请求和返回的响应都会经过这里,开发者可以在此集中实现日志记录、性能监控、安全拦截、数据预处理等通用逻辑,极大提升代码的复用性与可维护性。 FastAPI的中间件机制继承自Star
热门专题
热门推荐
Poe交换机带载后重启:是故障,还是系统在“自救”? 不少朋友遇到过这个头疼的问题:PoE交换机一接上设备就重启。其实,这本质上不是设备坏了,而是供电系统一套精密的自我保护机制在起作用。当负载接入的瞬间,如果系统检测到功耗超标、供电不稳等情况,就会主动触发复位,防止硬件受损。这正是IEEE 802
高性价比电饼铛:精准匹配、扎实可靠、真正省心 挑选一款高性价比的电饼铛,核心其实很明确:功能要精准匹配你的真实需求,材质工艺必须扎实可靠,细节设计能让你每天用着都省心。它追求的绝不是单纯的便宜或者参数漂亮,而是每一分钱都花在刀刃上。比如,2100W级的稳定火力保证了煎烤效率不打折;0氟不粘涂层配合蜂
红米K30 5G动态壁纸联网机制全解析 关于红米K30 5G的动态壁纸是否需要一直联网,答案是:完全没必要。这玩意儿用起来其实很“懂事”,它只在你第一次上手和偶尔想换新的时候,才需要网络搭把手。 其背后的逻辑很清晰:手机搭载的MIUI系统,把所有酷炫的动态壁纸资源都放在了小米官方的“云端仓库”里。所
vivo Y35桌面时间不显示?别急,这事儿有解 不少vivo Y35用户可能都遇到过这个情况:一觉醒来,或者换个主题之后,主屏幕上那个熟悉的“时间”不见了。先别急着怀疑手机坏了,事实是,超过八成的类似问题,根源其实很简单——时间组件压根没被“请”上桌面,或者相关的自动设置被无意中关闭了。作为一台搭
英雄联盟手游杰斯新皮肤外观设计酷炫,充满科技感。技能特效以蓝色能量为主,视觉效果震撼且辨识度高。实战中技能清晰、手感流畅,能提升操作自信与战场表现。整体而言,该皮肤在视觉、特效与实战体验上均表现优异,值得玩家入手。





