首页 / FastAPI 入门教程 / 依赖缓存

FastAPI 入门教程

依赖缓存

本教程共 50 篇 · 第 24 篇 · 更新于 2026-08-12 · 约 6 分钟阅读

FastAPIFastAPI 入门教程依赖缓存dependencies参数use_cache装饰器依赖请求级缓存性能优化

本节目标:学会在路径操作装饰器里声明”只想要副作用、不接收返回值”的依赖,并理解 FastAPI 对同一请求内重复依赖的缓存机制,以及何时关掉它。

前面两章,依赖的返回值都通过路径操作函数的参数接住。但有些依赖你想要的不是它的返回值,而是它”做事”的副作用。比如校验请求头、检查权限,校验完直接放行,路径操作里根本用不到那个值。

这一章讲两种相关技巧:装饰器的 dependencies 参数,以及依赖的缓存行为。

24-1 不需要返回值的依赖

有些依赖只要”被执行过”就行。比如一个检查请求头 X-Token 是否合法的依赖,合法就放行,不合法就报错。它的返回值路径操作函数压根用不上。

如果还按之前的方式写在参数里,编辑器会提示”这个参数没用到”,看着别扭。FastAPI 允许你把这类依赖放到路径操作装饰器上。

24-2 装饰器的 dependencies 参数

路径操作装饰器(@app.get(...) 等)接收一个 dependencies 参数,它是一组 Depends() 的列表。这些依赖照样会被执行、被解决,但它们的值不会传进路径操作函数。

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


async def verify_token(x_token: Annotated[str, Header()]) -> None:
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token 头无效")


async def verify_key(x_key: Annotated[str, Header()]) -> None:
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key 头无效")


@app.get(
    "/items/",
    dependencies=[Depends(verify_token), Depends(verify_key)],
)
async def read_items() -> dict:
    return [{"item": "Foo"}, {"item": "Bar"}]

这里 verify_tokenverify_key 都在装饰器里声明。每次访问 /items/,FastAPI 都会先执行它们:头不对就抛 400,对了就放行进入 read_items

因为依赖写在装饰器上,路径操作函数 read_items 的签名干干净净,没有”用不到的参数”。编辑器、新同事都不会误以为那个参数还有用。

Note

示例里的 X-TokenX-Key 是随便造的自定义请求头,只为演示。真实项目做鉴权时,建议直接用 FastAPI 内置的 Security 工具(后面安全章节会讲),收益更大。

24-3 装饰器依赖能用普通依赖

装饰器里的依赖,和你平时用的依赖函数是同一套东西。它们能声明请求参数(如 Header)、能挂子依赖、能抛异常、也能返回值(只是返回值不用而已)。

这意味着你完全可以把一个已经在别处用、且有返回值的依赖,也挂到装饰器上。它照样被执行,只是这次的返回值被丢弃。

24-4 同一组接口的依赖

当一个大项目拆成多个文件、多个接口属于同一组时,可以给整组接口统一加 dependencies。等讲到”大型应用多文件”结构时,你会看到怎么给一组路由一次性声明公共依赖,而不用每条都写一遍。

这就是装饰器依赖的延伸价值:把”横切”的逻辑(日志、限流、公共校验)集中声明。

24-5 依赖的缓存机制是什么

现在讲另一个重要机制:同一请求内,同一个依赖默认只执行一次

想象你有两条子依赖都依赖同一个底层的 get_value。FastAPI 在解决依赖树时,发现 get_value 被多处需要,它不会傻乎乎地调两遍,而是调用一次、把结果缓存起来,再分发给所有需要它的地方。

这在官方文档里就叫 cache(缓存)。它针对的是”同一个请求”,不是跨请求的全局缓存,别和 Redis 那种搞混。

24-6 缓存带来的好处

缓存最直接的好处是省性能。典型的例子是”解析当前用户”:

from typing import Annotated

from fastapi import Depends

app = FastAPI()


async def get_current_user(token: Annotated[str, Header()]) -> dict:
    # 假设这里要去数据库或调鉴权服务查用户
    user = {"username": "alice", "token": token}
    return user

如果 get_current_userread_profileupdate_profile 等多个子依赖共同需要,没有缓存就要查库好几遍。有了请求级缓存,同一请求里只查一次,后面的依赖直接复用结果。

Tip

凡是”计算贵、但同一请求内结果不变”的依赖,都该享受这个缓存。数据库连接、配置加载、权限解析,都是典型收益场景。

24-7 用 use_cache 关掉缓存

有些情况你偏偏想让依赖每次都重新执行,不要复用缓存值。这时给 Dependsuse_cache=False

from typing import Annotated

from fastapi import Depends


async def get_value() -> str:
    return "某个可能每次都变的值"


async def needy_dependency(
    fresh_value: Annotated[str, Depends(get_value, use_cache=False)],
) -> dict:
    return {"fresh_value": fresh_value}

use_cache=False 告诉 FastAPI:别缓存这个依赖,同一请求里每用到一次就真的再调一次。默认是 use_cache=True,也就是会缓存。

绝大多数场景用默认即可。只有当你确信依赖的每次结果都必须是最新、且缓存会出错时,才显式关掉它。

24-8 缓存与子依赖配合

缓存对子依赖尤其划算。前面第 23 章讲过子依赖链:一个 query_extractor 被多个上层依赖共用。在同一个请求里,FastAPI 只调用一次 query_extractor,把结果喂给所有需要它的上层。

你写代码时不用操心”会不会重复算”,框架已经替你做了优化。你要做的只是正常声明依赖关系,缓存自动生效。

24-9 什么时候该用哪种写法

简单总结一下选择:

  • 路径操作里要用依赖的返回值 → 写在路径操作函数参数里,用 Depends(...)
  • 路径操作里不关心返回值、只要它执行过 → 写在装饰器 dependencies=[...]
  • 依赖结果在同一请求内不该变、且计算贵 → 用默认缓存(什么都不用写)
  • 依赖结果必须每次都重新算 → 加 use_cache=False

这两类技巧都属于依赖注入的”基础设施”,配合前面的类依赖、子依赖,能搭出很清晰的结构。

24-10 小结

装饰器的 dependencies 参数用来声明”只执行、不取值”的依赖,常做校验和副作用,让路径操作签名更干净。同一请求内,FastAPI 默认把重复依赖的结果缓存起来,只算一次,省性能。需要每次都重新执行时,用 use_cache=False 关掉缓存。下一章我们看依赖还能用 yield 管理资源。