FastAPI 系列

  1. FastAPI 系列:从第一个接口到生产部署

  2. FastAPI 为什么适合做 JSON API

  3. 第一个接口怎么跑起来

  4. 路径、查询和请求体为什么要分开声明

  5. Pydantic 模型怎么当契约

  6. 路由怎么拆

  7. 依赖注入到底省了什么(当前篇)

  8. 数据库会话怎么进接口

  9. 状态码和异常为什么不要裸 raise

  10. 鉴权怎么接到 Depends 上

  11. 中间件、CORS 和后台任务分别解决什么

  12. 测试怎么写才不连真实库

  13. 生产怎么部署

路径函数不该自己去打开数据库、解析 Token、读配置文件。这些步骤声明成依赖,FastAPI 在调用前解析,调用后清理。省下的不是几行代码,而是可测试的边界。

请求进入后先解析 Depends 链,再执行路径函数并在 yield 后清理

最简单的依赖

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()


def get_settings():
    return {"env": "dev"}


@app.get("/info")
async def info(settings: Annotated[dict, Depends(get_settings)]):
    return settings

Depends(get_settings) 的意思是:先调用 get_settings,把返回值注入参数。同一请求里同一个依赖默认会缓存,不会无意义地执行两遍。

yield:请求前准备,请求后清理

async def get_db():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()

yield 之前相当于进入 with,之后相当于退出。路径函数跑完、异常抛出后,finally 仍会执行。这是数据库会话、文件句柄、HTTP 客户端的正确位置。

注意:yield 只调用一次。不要在依赖里写两个 yield

依赖可以再依赖

from typing import Annotated

from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
    return fake_decode(token)


async def get_current_active_user(
    user: Annotated[User, Depends(get_current_user)],
):
    if user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return user

路径函数只声明「我需要当前活跃用户」。Token 怎么取、怎么解码,全部藏在依赖链里。鉴权篇会把 JWT 接到这条链上。

什么该进 Depends,什么不该

放进 Depends

留在路径函数

DB 会话、Redis、配置

具体业务计算

当前用户、权限检查

资源是否存在、是否允许修改这一条

分页的公共解析

这一次查询怎么拼 SQL

常见误区

在路径函数里全局用一个 Session

并发请求会串数据。会话按请求创建,用 yield 关闭。

把 Depends 当成服务定位器到处乱调用

依赖树应浅而明确。十层嵌套会让请求变慢,也难读。

忽略 yield 之后的异常

路径函数抛出后,依赖仍可在 except 里 rollback 再 raise。下一篇接数据库时会写完整模式。

小结

Depends 把「请求生命周期里的公共准备」从业务函数里拆走。测试篇会用 dependency_overrides 换掉这些依赖。下一篇把它接到 SQLAlchemy 会话上。

参考资料