fastapi 依赖注入机制

"""
第三步:依赖注入(Depends)

目标:看穿 fastapi 最"黑魔法"的特性 —— 它本质只是"递归地解析函数签名"。

机制:

  1. Depends(f) 只是一个"标记对象",把它放在参数默认值的位置。
  2. 框架解析参数时:
    • 普通参数 -> 从请求里取(第二步做过的)
    • 默认值是 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
posted @ 2026-07-03 12:40  RolandHe  阅读(5)  评论(0)    收藏  举报