fastapi 依赖注入机制
"""
第三步:依赖注入(Depends)
目标:看穿 fastapi 最"黑魔法"的特性 —— 它本质只是"递归地解析函数签名"。
机制:
- Depends(f) 只是一个"标记对象",把它放在参数默认值的位置。
- 框架解析参数时:
- 普通参数 -> 从请求里取(第二步做过的)
- 默认值是 Depends(f) 的参数 -> 改为"调用 f 取值"
而 f 自己的参数,又用同样的规则递归解析。
"""
import inspect
class Depends:
"""一个标记对象。它不干活,只是"携带"了要调用的那个依赖函数。"""
def __init__(self, dependency):
self.dependency = dependency
class App:
def __init__(self):
self.routes = {}
def get(self, path):
def decorator(func):
self.routes[("GET", path)] = func
return func
return decorator
def solve(self, func, raw_params: dict):
"""
解析 func 需要的所有参数,返回一个 kwargs 字典。
这是一个递归函数:依赖的依赖,会再次调用 solve。
"""
kwargs = {}
sig = inspect.signature(func)
for name, param in sig.parameters.items():
default = param.default
if isinstance(default, Depends):
# 这个参数是个"依赖":不从请求取,而是递归解析并调用依赖函数
dep_func = default.dependency
dep_kwargs = self.solve(dep_func, raw_params) # <-- 递归!
kwargs[name] = dep_func(**dep_kwargs)
else:
# 普通参数:从请求里取 + 按类型转换(第二步的逻辑)
raw_value = raw_params.get(name)
if raw_value is None:
if default is not inspect.Parameter.empty:
kwargs[name] = default # 用默认值
else:
raise ValueError(f"缺少参数: {name}")
else:
type_ = param.annotation
if type_ is inspect.Parameter.empty:
kwargs[name] = raw_value
else:
kwargs[name] = type_(raw_value)
return kwargs
def handle(self, method, path, raw_params: dict):
func = self.routes.get((method, path))
if func is None:
return "404 Not Found"
try:
kwargs = self.solve(func, raw_params)
except ValueError as e:
return f"422 {e}"
return func(**kwargs)
app = App()
# ---- 一条依赖链:profile 依赖 current_user,current_user 依赖 token ----
def get_token(token: str):
# 普通参数 token,会从请求里取
return token
def get_current_user(tok=Depends(get_token)):
# 它自己的参数 tok 又是一个依赖 -> 触发递归
return f"user_of_{tok}"
@app.get("/profile")
def profile(user=Depends(get_current_user)):
return f"profile of [{user}]"
@app.get("/add")
def add(x: int, y: int):
# 普通路由,验证第二步的功能没被破坏
return x + y
if __name__ == "__main__":
print('/profile token="abc123" ->',
app.handle("GET", "/profile", {"token": "abc123"}))
print('/add x="2" y="3" ->',
app.handle("GET", "/add", {"x": "2", "y": "3"}))
输出结果:
/profile token="abc123" -> profile of [user_of_abc123]
/add x="2" y="3" -> 5

浙公网安备 33010602011771号