FastAPI 面试必会:ASGI、Pydantic、Depends 和异步请求链路一次讲清
很多人学 FastAPI,只记住了 `@app.get()`、`async def` 和自动生成 Swagger。但真正面试和做 Agent 后端时,更重要的是理解一次请求从 Uvicorn 进入 ASGI,到 Starlette 路由匹配,再到 FastAPI 解析函数签名、调用 Depends、用 Pydantic 校验数据,最后返回响应的完整链路。本文从 Java 后端视角拆解 FastAP
FastAPI 面试必会:ASGI、Pydantic、Depends 和异步请求链路一次讲清

开场:别只把 FastAPI 背成“Python 版 Spring Boot”
前几篇我们已经把 Python 对象模型、装饰器、生成器、上下文管理器、GIL、线程、进程、协程和 asyncio 讲完了。
今天终于可以接到后端框架:FastAPI。
很多 Java 后端刚看 FastAPI,会有一个很自然的类比:
Spring Boot:
Controller -> 参数绑定 -> Validation -> Service -> Response
FastAPI:
@app.get -> 参数绑定 -> Pydantic -> Depends -> Response
这个类比能帮你快速入门,但如果只停在这里,面试很容易被追问卡住:
- FastAPI、Starlette、Uvicorn、Pydantic 分别负责什么?
- ASGI 和 WSGI 的区别到底在哪里?
- 为什么函数参数写个类型,框架就知道从 path、query、body 里取值?
Depends是不是和 Spring 的@Autowired一样?async def一定比def快吗?- 在 Agent 后端里,为什么 FastAPI 经常和 SSE、WebSocket、流式响应一起出现?
这篇不写 Hello World,也不按 API 清单背。
我们直接抓住一条主线:
FastAPI 的核心不是“装饰器写路由”,而是把 Python 类型标注、ASGI 异步协议、Pydantic 数据校验和函数式依赖注入串成一条请求处理链路。
读完以后,你应该能把 FastAPI 讲成一套后端机制,而不是一组语法糖。
一、先看四层分工:Uvicorn、ASGI、Starlette、FastAPI、Pydantic
先把几个名字拆开。

1. Uvicorn:负责把网络请求变成 ASGI 调用
Uvicorn 是 ASGI Web Server。
它更像 Java 里的 Tomcat、Netty 服务入口,负责监听端口、接收 HTTP/WebSocket 连接,然后按照 ASGI 规范调用应用。
ASGI 应用的底层形状大概是这样:
async def app(scope, receive, send):
...
三个参数很关键:
scope:连接信息,比如协议类型、路径、方法、请求头。receive:从客户端接收消息的异步通道。send:向客户端发送响应消息的异步通道。
所以 ASGI 的重点不是“异步”这个词本身,而是它把 Web Server 和应用框架之间的交互标准化了。
Uvicorn 可以跑 FastAPI,也可以跑其他 ASGI 框架。FastAPI 本身不负责监听 socket。
2. ASGI:异步 Web 应用和服务器之间的协议
以前 Python Web 常讲 WSGI。
WSGI 应用是同步 callable,适合传统短请求响应。它对 WebSocket、长连接、流式响应这类场景支持得不自然。
ASGI 则把连接抽象成 scope + receive + send。这让框架可以处理:
- 普通 HTTP 请求。
- WebSocket 长连接。
- 长轮询。
- 流式响应。
- 需要等待网络 IO 的异步任务。
这也是 FastAPI 特别适合大模型应用后端的原因之一。LLM 接口经常不是“请求进来,算完一次性返回”,而是边生成边返回、连接保持一段时间、甚至双向通信。
3. Starlette:负责 Web 层能力
FastAPI 建在 Starlette 之上。
Starlette 提供的是 Web 框架底座,包括:
- 路由。
- Request / Response 对象。
- Middleware。
- WebSocket。
- 静态文件。
- 异常处理。
- 测试客户端。
- 线程池处理同步端点等。
你可以把 Starlette 理解为 FastAPI 的 Web 内核。
FastAPI 不是从零实现所有 HTTP 能力,而是在 Starlette 的基础上加了更强的 API 开发体验。
4. Pydantic:负责数据校验、类型转换和 schema
Pydantic 是数据校验库。
FastAPI 用它来处理请求体、响应模型、类型转换、错误信息和 JSON Schema。
比如你写:
from pydantic import BaseModel
class CreateUserRequest(BaseModel):
name: str
age: int
FastAPI 不只是把它当成普通 class。它会把请求 JSON 交给 Pydantic:
- 字段是否存在。
- 类型能否转换。
- 是否满足约束。
- 错误应该定位到哪个字段。
- OpenAPI 文档里的 schema 怎么生成。
截至 2026-06-09,Pydantic 官方文档页面显示的版本是 v2.13.4。现在写 FastAPI 文章和项目时,要默认按 Pydantic v2 的思路理解,不要再把旧版 Config、orm_mode 等写法当成唯一标准。
5. FastAPI:把类型、路由、依赖和文档串起来
FastAPI 真正做的是胶水层和增强层:
- 读取路由函数签名。
- 根据参数位置和声明判断数据来源。
- 调 Pydantic 做转换和校验。
- 解析
Depends依赖图。 - 处理
def/async def调用方式。 - 生成 OpenAPI 和交互式文档。
- 复用 Starlette 的 Web 能力。
一句话总结:
Uvicorn 管网络入口,ASGI 定通信协议,Starlette 管 Web 机制,Pydantic 管数据契约,FastAPI 把这些能力组合成面向 API 的开发模型。
二、一次请求在 FastAPI 里怎么走?
面试里如果被问“讲一下 FastAPI 请求生命周期”,不要只说“先到路由,再到函数”。
可以按下面这条链路回答。

第一步:Uvicorn 接收连接,构造 ASGI 事件
用户请求进来:
GET /users/100?verbose=true
Uvicorn 接收连接后,会把这次请求整理成 ASGI 的 scope,里面包含:
type:httpmethod:GETpath:/users/100query_string:verbose=trueheaders: 请求头client: 客户端地址
请求体不是一次性强塞给应用,而是可以通过 receive() 异步读取。
这就是流式请求体和异步 IO 的基础。
第二步:Starlette 中间件包住整个应用
请求进入应用后,会先经过 middleware 链。
中间件适合做“所有请求都要经过”的横切逻辑:
- CORS。
- 请求日志。
- Trace ID。
- 统一耗时统计。
- 全局安全头。
- 全局异常包装。
- 代理头、HTTPS 重定向等基础设施逻辑。
它的位置很靠外,能看到原始 request,也能包住 response。
所以它适合处理“全局通道层逻辑”,不适合承载太多业务判断。
第三步:Starlette / FastAPI 做路由匹配
然后框架根据 method 和 path 找到对应 endpoint。
比如:
@app.get("/users/{user_id}")
async def get_user(user_id: int, verbose: bool = False):
...
/users/100 会匹配到 /users/{user_id}。
但到这里还没有直接调用你的函数。FastAPI 还要先分析函数签名。
第四步:FastAPI 解析函数签名
FastAPI 会看这个函数:
async def get_user(user_id: int, verbose: bool = False):
...
它会判断:
user_id出现在路径模板里,所以来自 path。verbose没出现在路径里,也不是 body model,所以默认来自 query。user_id: int表示要转成整数。verbose: bool = False表示 query 参数可选,默认False。
这就是 FastAPI 看起来“魔法”的地方。
其实它不是凭空猜,而是在利用 Python 的类型标注、默认值和 Annotated 元数据。
更完整的写法会是:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(
user_id: int,
verbose: Annotated[bool, Query(description="是否返回详细信息")] = False,
):
return {"user_id": user_id, "verbose": verbose}
第五步:Pydantic 做转换和校验
HTTP 请求里的 path、query、header 本质上都是字符串。
FastAPI 看到 user_id: int,会尝试把字符串 "100" 转成整数 100。
如果用户传的是:
/users/foo
它会返回结构化校验错误,而不是让你的业务函数收到一个脏值。
请求体也是一样。
from pydantic import BaseModel, Field
class CreateOrderRequest(BaseModel):
sku_id: str
count: int = Field(gt=0, le=100)
remark: str | None = None
@app.post("/orders")
async def create_order(req: CreateOrderRequest):
return req
这里 FastAPI 会:
- 读取请求体 JSON。
- 转换字段类型。
- 校验
count范围。 - 把校验后的对象传给
req。 - 生成 OpenAPI schema。
- 在
/docs里展示接口文档。
这就是为什么 FastAPI 文章里总强调 type hints。
它不是为了“看起来有类型”,而是让函数签名变成 API 契约。
第六步:解析 Depends 依赖图
如果函数里有 Depends:
from typing import Annotated
from fastapi import Depends, Header, HTTPException
async def get_token(authorization: Annotated[str | None, Header()] = None) -> str:
if not authorization:
raise HTTPException(status_code=401, detail="Missing token")
return authorization.removeprefix("Bearer ").strip()
@app.get("/me")
async def me(token: Annotated[str, Depends(get_token)]):
return {"token": token}
FastAPI 会先调用 get_token,拿到返回值,再把它注入 me()。
依赖函数本身也可以继续声明参数和依赖,形成一张依赖图:
endpoint
├─ get_current_user
│ └─ get_token
└─ get_db_session
这和 Spring 的 Bean 容器有相似处,但不要完全等同。
FastAPI 的依赖注入更偏函数式:
- 依赖通常是函数、类或可调用对象。
- 很多依赖按请求解析。
- 依赖可以拿 path/query/header/body。
- 依赖可以嵌套。
- 依赖可以配合
yield做资源释放。 - 测试时可以覆盖依赖。
它不是一个完整的企业级 IoC 容器,更像“围绕请求函数签名构建的依赖解析系统”。
第七步:调用 endpoint,区分 def 和 async def
到这里,参数和依赖都准备好了,才会真正调用你的业务函数。
如果 endpoint 是:
@app.get("/items")
async def list_items():
...
FastAPI 可以在事件循环里 await 它。
如果 endpoint 是:
@app.get("/legacy")
def legacy_api():
...
FastAPI 不会直接在事件循环里执行它,而是把它放到外部线程池里运行,再等待结果。
官方文档里也明确说了:普通 def 的 path operation function 会在外部 threadpool 中运行,避免阻塞 server。
这点很重要,下一节单独讲。
第八步:序列化响应,走回 ASGI send
你的函数返回:
return {"user_id": 100, "name": "Ming"}
FastAPI 会把返回值转成响应对象,必要时用 response model 做输出校验和序列化,再交给 Starlette Response,最终通过 ASGI 的 send 发回客户端。
如果是流式响应,就不是一次性发完,而是多次发送 body chunk。
这也是 Agent 后端常用 SSE 或 StreamingResponse 的基础。
三、路径、查询、请求体:FastAPI 参数绑定的判断规则
FastAPI 参数绑定最容易混,但规则其实很稳定。

1. 出现在路径模板里的参数,来自 Path
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
item_id 出现在 /items/{item_id} 中,所以它来自路径。
如果传入 /items/foo,而类型要求是 int,框架会返回校验错误。
2. 简单类型且不在路径里,默认来自 Query
@app.get("/items")
async def list_items(skip: int = 0, limit: int = 20):
return {"skip": skip, "limit": limit}
请求:
/items?skip=10&limit=50
skip 和 limit 来自 query。
它们在 URL 里天然是字符串,但 FastAPI 会按类型转换并校验。
3. Pydantic model 通常来自 Body
class UpdateItemRequest(BaseModel):
name: str
price: float
@app.put("/items/{item_id}")
async def update_item(item_id: int, req: UpdateItemRequest):
return {"item_id": item_id, "item": req}
item_id 来自 path,req 来自 body。
这套规则的面试说法可以是:
FastAPI 根据路由模板、函数参数、类型标注、默认值和额外声明,决定参数来自 path、query、header、cookie、body、form 还是 dependency。
如果要显式声明,就用:
from typing import Annotated
from fastapi import Body, Header, Path, Query
async def demo(
item_id: Annotated[int, Path(gt=0)],
q: Annotated[str | None, Query(max_length=50)] = None,
trace_id: Annotated[str | None, Header()] = None,
payload: Annotated[dict | None, Body()] = None,
):
...
实际项目里,建议优先用 Annotated,它更清楚地把“类型”和“参数元数据”分开。
四、Depends:它解决的是“每个请求怎么拿到上下文”
很多人第一次看 Depends,会把它理解成 Spring 的 @Autowired。
这个理解只对了一半。
在 FastAPI 里,Depends 更常用于解决“这次请求需要哪些上下文”:
- 当前用户是谁。
- token 是否有效。
- 当前租户是什么。
- 数据库 session 怎么创建和释放。
- 这个接口需要什么权限。
- 当前请求的 trace id / request id 是什么。
- 某个 route 需要哪个 service 或 client。
一个更像真实项目的 Depends 写法
from collections.abc import AsyncIterator
from typing import Annotated
from fastapi import Depends, Header, HTTPException
from pydantic import BaseModel
class User(BaseModel):
id: str
role: str
async def get_token(authorization: Annotated[str | None, Header()] = None) -> str:
if not authorization:
raise HTTPException(status_code=401, detail="Missing Authorization")
return authorization.removeprefix("Bearer ").strip()
async def get_current_user(token: Annotated[str, Depends(get_token)]) -> User:
# 真实项目里这里会查缓存、认证服务或 JWT
if token == "admin-token":
return User(id="u_1", role="admin")
raise HTTPException(status_code=403, detail="Invalid token")
async def get_db() -> AsyncIterator[str]:
db = "db-session"
try:
yield db
finally:
# 真实项目里这里关闭 session / rollback / release connection
pass
@app.get("/admin/orders")
async def admin_orders(
user: Annotated[User, Depends(get_current_user)],
db: Annotated[str, Depends(get_db)],
):
if user.role != "admin":
raise HTTPException(status_code=403, detail="Forbidden")
return {"db": db, "user": user.id}
这段代码里,Depends 做了三件事:
- 把认证逻辑从 endpoint 里拿出去。
- 把 token、user、db session 组成依赖图。
- 让资源释放逻辑可以跟请求生命周期绑定。
这就是它比“工具函数调用”更有价值的地方。
工具函数只能被你手动调用。
依赖函数会被框架识别、排序、缓存、注入、清理,也能在测试中覆盖。
Depends 和 Middleware 怎么选?
这是 Agent 后端面试里很常见的问题。
可以用一个简单判断:
所有请求都要经过,且与具体路由关系不大 -> Middleware
某些接口需要,且要把结果注入业务函数 -> Depends
比如:
| 场景 | 更适合 |
|---|---|
| CORS | Middleware |
| 请求耗时统计 | Middleware |
| Trace ID 注入到 request.state | Middleware |
| 全局安全响应头 | Middleware |
| 当前用户解析 | Depends |
| 权限校验 | Depends |
| 数据库 session | Depends |
| 当前租户上下文 | Depends |
| LLM client / retriever / service 注入 | Depends |
在大模型接口里,一个比较稳的组合是:
- Middleware 负责 trace id、日志、耗时、跨域、全局异常边界。
- Depends 负责用户、权限、租户、数据库 session、向量库 client、模型服务 client。
- Lifespan 负责应用启动时初始化连接池、模型资源、长期复用的客户端。
不要把所有逻辑都塞进中间件。中间件太外层,业务上下文弱,测试和路由级差异会变难。
也不要把所有逻辑都塞进 Depends。全局日志、CORS、响应头这种通道逻辑放 Depends 里会重复且不自然。
五、def 和 async def:不是谁高级,而是看调用链
上一篇讲 asyncio 时已经埋了伏笔:async def 不是加速器。
FastAPI 官方文档给出的判断非常直接:
- 如果你调用的第三方库需要
await,就用async def。 - 如果你调用的库是同步阻塞库,没有
await支持,就用普通def。 - 如果不知道,用普通
def更稳。 - FastAPI 允许同一个项目里混用
def和async def。

正确例子:异步 HTTP 客户端
import httpx
@app.get("/profile/{user_id}")
async def profile(user_id: str):
async with httpx.AsyncClient(timeout=3) as client:
resp = await client.get(f"https://user-service/users/{user_id}")
return resp.json()
这里网络 IO 支持 await,所以 async def 是合适的。
请求等待下游服务时,事件循环可以去处理其他请求。
危险例子:在 async def 里调用阻塞库
import requests
@app.get("/profile/{user_id}")
async def profile(user_id: str):
resp = requests.get(f"https://user-service/users/{user_id}", timeout=3)
return resp.json()
这段代码看起来是 async,但里面的 requests.get() 是同步阻塞调用。
结果是:事件循环被卡住。
这就是典型“伪异步”。
如果短期内只能用同步库,更稳的写法是:
import requests
@app.get("/profile/{user_id}")
def profile(user_id: str):
resp = requests.get(f"https://user-service/users/{user_id}", timeout=3)
return resp.json()
FastAPI 会把普通 def endpoint 放到线程池里执行,避免直接堵住事件循环。
线程池不是无限的
这里还有一个坑。
Starlette 文档里写得很清楚:它会在若干场景使用线程池来避免阻塞事件循环,包括同步 endpoint、文件响应、上传文件、同步后台任务等。
但默认线程池 capacity limiter 只有 40 个 token。FastAPI 的同步依赖也会消耗这个线程容量。
所以不能误以为:
我全部写
def,框架会自动帮我无限扩容。
同步阻塞调用多了以后,线程池会成为瓶颈。
真正高吞吐的接口,要么使用异步库,要么把重活交给进程池、任务队列或专门的 worker。
CPU 密集任务不要靠 async 硬扛
比如:
- 大文件解析。
- 图片处理。
- 本地模型推理。
- 向量批量计算。
- CPU 密集 rerank。
这些任务如果直接写在 endpoint 里,会拖垮请求线程或事件循环。
更稳的方案是:
- 短小 CPU 任务:考虑进程池。
- 长耗时任务:交给任务队列。
- 模型推理:独立推理服务或专门 worker。
- 在线接口:只做参数校验、任务提交、状态查询、流式转发。
面试回答时可以这样收:
FastAPI 的 async 优势主要体现在高并发 IO,不是让 CPU 计算自动并行。
async def里必须一路使用可 await 的库,否则就是伪异步;同步库用def或显式线程池,CPU 重活交给进程池或后台 worker。
六、Lifespan:初始化模型、连接池和客户端,不要塞进全局裸变量
FastAPI 以前常见 startup / shutdown 事件,现在官方更推荐用 lifespan。
lifespan 的形状像一个异步上下文管理器:
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.model_client = await create_model_client()
app.state.vector_store = await create_vector_store()
yield
await app.state.model_client.aclose()
await app.state.vector_store.aclose()
app = FastAPI(lifespan=lifespan)
这和前面讲过的上下文管理器可以连起来理解:
yield前:应用启动时执行。yield后:应用关闭时执行。- 中间:应用正常处理请求。
Agent 后端里,lifespan 很适合放:
- LLM API client。
- 向量数据库连接池。
- Redis 连接池。
- 数据库 engine。
- embedding / rerank 组件。
- 需要复用的工具注册表。
然后通过 Depends 读取:
from fastapi import Depends, Request
def get_model_client(request: Request):
return request.app.state.model_client
@app.post("/chat")
async def chat(client = Depends(get_model_client)):
...
注意一个生产细节:如果你用多个 worker 进程,每个进程都会有自己的应用实例和 lifespan 执行过程。不要以为全局变量天然就是全局唯一资源。
七、流式响应:Agent 后端为什么喜欢 FastAPI
传统 CRUD 接口通常是:
request -> 等业务处理完 -> response
但大模型接口更常见的是:
request -> 模型开始生成 -> token/chunk 持续返回 -> 完成
这时一次性 JSON 响应体验很差。
FastAPI 可以用 StreamingResponse:
from collections.abc import AsyncIterator
from fastapi.responses import StreamingResponse
async def stream_answer(prompt: str) -> AsyncIterator[bytes]:
async for chunk in llm_client.stream(prompt):
yield f"data: {chunk}\n\n".encode("utf-8")
@app.get("/chat/stream")
async def chat_stream(q: str):
return StreamingResponse(
stream_answer(q),
media_type="text/event-stream",
)
这里的关键点是:生成器里要有真正的 await 让出事件循环。
FastAPI 官方文档在 StreamingResponse 部分也提醒:异步任务只有到达 await 时才有机会处理取消。如果大流或无限流里没有 await,客户端断开后任务可能不能及时取消。
如果需要双向通信,比如浏览器持续发送用户输入,服务端持续推送状态,就可以用 WebSocket:
from fastapi import WebSocket
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
message = await websocket.receive_text()
await websocket.send_text(f"echo: {message}")
面试里可以这样区分:
- 普通 JSON:一次请求一次响应。
- StreamingResponse / SSE:服务端持续向客户端推送,适合 LLM token 流。
- WebSocket:双向长连接,适合实时聊天、协作、状态同步。
不要为了“高级”强行上 WebSocket。很多大模型聊天接口,SSE 已经够用,部署和调试也更简单。
八、FastAPI 常见面试坑

坑 1:以为加了 async 就不会阻塞
async def 只是允许你在函数里使用 await。
如果里面调用的是同步阻塞库,事件循环照样会被卡住。
判断标准不是函数前面有没有 async,而是慢操作有没有可 await 的让出点。
坑 2:把 Depends 当成全局单例容器
FastAPI 的 Depends 不是 Spring Bean 容器的完整替代品。
它更适合围绕请求做依赖解析。
对于连接池、模型 client 这种生命周期更长的资源,建议用 lifespan 初始化,然后 Depends 读取,而不是在依赖函数里每次重新创建。
坑 3:把业务权限全塞 Middleware
Middleware 适合全局逻辑。
如果某些路由才需要管理员权限、某些路由需要租户上下文、某些路由允许匿名访问,Depends 往往更合适。
坑 4:把 Pydantic model 当 ORM 实体
Pydantic model 适合做接口输入输出的数据契约。
ORM model 负责数据库映射。
两者可以转换,但不建议混成一个东西。否则接口字段、数据库字段、权限脱敏、内部状态很容易纠缠。
坑 5:把 BackgroundTasks 当可靠任务队列
FastAPI 的 BackgroundTasks 适合响应返回后做轻量附加动作,比如写日志、发送非关键通知。
如果任务要求可靠重试、状态追踪、长耗时执行、失败恢复,就应该考虑专门的任务队列或 worker。
坑 6:不知道线程池容量会成为瓶颈
同步 endpoint 和同步 dependency 会进线程池。
线程池能保护事件循环,但不是无限资源。同步阻塞代码过多时,吞吐会被线程池容量、下游连接池和 CPU 一起限制。
九、面试回答模板:用一条链路讲清 FastAPI
最后给一个可以直接背、也经得起追问的回答框架。

如果面试官问:
你讲一下 FastAPI 的请求处理流程。
可以这样答:
FastAPI 应用一般由 Uvicorn 这类 ASGI server 启动。
Uvicorn 接收 HTTP 或 WebSocket 连接后,会按 ASGI 协议把请求变成 scope、receive、send 三个对象。
进入应用后,请求先经过 Starlette/FastAPI 的中间件链,再做路由匹配。
匹配到 endpoint 后,FastAPI 会分析函数签名,根据路径模板、参数类型、默认值和 Annotated 元数据判断参数来自 path、query、header、cookie、body 还是 dependency。
请求数据会交给 Pydantic 做类型转换和校验,校验失败会返回结构化错误。
如果 endpoint 声明了 Depends,FastAPI 会先解析依赖图,执行依赖并把结果注入函数。
最后 FastAPI 根据 endpoint 是 def 还是 async def 决定调用方式:
async def 可以直接 await;
普通 def 会放到线程池,避免阻塞事件循环。
函数返回后,FastAPI/Starlette 把结果序列化成 Response,再通过 ASGI send 发回客户端。
如果面试官继续问:
中间件和 Depends 怎么选?
可以答:
中间件适合全局通道逻辑,比如 CORS、日志、Trace ID、耗时统计、统一响应头。
Depends 适合路由上下文逻辑,比如当前用户、权限、租户、数据库 session、模型 client。
我的判断是:所有请求都要经过、且不强依赖具体业务路由的,放中间件;
只有部分接口需要、并且需要把结果注入业务函数的,放 Depends。
如果问:
Agent 后端为什么常用 FastAPI?
可以答:
因为 Agent 后端通常有三类需求:
第一,接口契约变化快,FastAPI 的类型标注、Pydantic 校验和 OpenAPI 文档很适合快速迭代。
第二,LLM、向量库、工具服务大多是网络 IO,FastAPI 基于 ASGI 和 async/await,适合高并发 IO。
第三,LLM 输出常常需要流式返回,FastAPI 可以用 StreamingResponse/SSE,也支持 WebSocket。
但我不会简单把所有接口都写 async def。是否 async 取决于调用链是否真正支持 await;同步阻塞库用 def 或线程池,CPU 重活交给 worker。
这套回答比“FastAPI 很快、自动生成文档、支持异步”更有信息量。
它说明你理解框架,也理解后端运行时。
总结:FastAPI 要按请求链路理解
FastAPI 最值得掌握的不是某个装饰器,而是这张心智图:
Uvicorn
-> ASGI scope / receive / send
-> Starlette middleware / routing
-> FastAPI signature analysis
-> Path / Query / Header / Body binding
-> Pydantic validation
-> Depends dependency graph
-> def threadpool or async def await
-> Response serialization
-> ASGI send
把这条链路讲清楚,很多问题都会变简单:
- 参数从哪里来?
- 为什么校验错误能定位字段?
- Depends 到底解决什么?
- 中间件和依赖怎么分工?
- async 为什么不是万能的?
- Agent 接口为什么适合流式响应?
如果你是 Java 后端转 Python / FastAPI,不要只把它类比成 Spring Boot。
更准确的理解是:
FastAPI 是一套基于 ASGI 的 API 框架,它用 Python 类型系统描述接口契约,用 Pydantic 执行数据校验,用 Starlette 承接 Web 层能力,用 Depends 把请求上下文组织成可测试、可复用的依赖图。
这才是面试和项目里真正能用上的 FastAPI。
参考资料
- FastAPI 官方文档首页:https://fastapi.tiangolo.com/
- FastAPI Concurrency and async / await:https://fastapi.tiangolo.com/async/
- FastAPI Path Parameters:https://fastapi.tiangolo.com/tutorial/path-params/
- FastAPI Query Parameters:https://fastapi.tiangolo.com/tutorial/query-params/
- FastAPI Request Body:https://fastapi.tiangolo.com/tutorial/body/
- FastAPI Dependencies:https://fastapi.tiangolo.com/tutorial/dependencies/
- FastAPI Lifespan Events:https://fastapi.tiangolo.com/advanced/events/
- FastAPI WebSockets:https://fastapi.tiangolo.com/advanced/websockets/
- FastAPI StreamingResponse:https://fastapi.tiangolo.com/advanced/custom-response/
- Uvicorn ASGI Concepts:https://www.uvicorn.org/concepts/asgi/
- Uvicorn 官方文档:https://www.uvicorn.org/
- Starlette Thread Pool:https://starlette.dev/threadpool/
- Starlette Requests:https://www.starlette.io/requests/
- Starlette Middleware:https://www.starlette.io/middleware/
- Pydantic 官方文档:https://docs.pydantic.dev/latest/
更多推荐


所有评论(0)