async def 是硬性要求,因为FastAPI依赖uvicorn的asyncio事件循环,普通def会阻塞协程调度器;必须用httpx.AsyncClient等异步库替代requests,且需复用client实例;中间件读body后须存入request.state避免重复消费;数据库驱动也需真异步如asyncpg。

必须用 async def 定义路由,否则哪怕只调用一次 requests.get() 或 time.sleep(),整个事件循环就卡死——并发能力直接归零。
为什么 async def 是硬性要求,不是可选项
FastAPI 的异步能力完全依赖于 uvicorn 的 asyncio 事件循环。如果写成普通 def,函数体哪怕只有一行同步 I/O(比如读文件、查 SQLite、调 json.loads()),也会阻塞整个协程调度器,后续所有请求排队等待。
- 常见错误现象:
RuntimeError: There is no current event loop in thread 'ThreadPoolExecutor-0_0',多出现在开了--workers后在模块顶层初始化了asyncio.EventLoop - 即使当前逻辑没 await 任何东西,也必须声明为
async def;这是让 uvicorn 把它当协程调度的前提 - 返回值类型提示(如
-> dict或-> List[Item])不能省,FastAPI 靠它做自动序列化和文档生成
httpx.AsyncClient 替代 requests 是最常漏掉的一步
很多人写了 async def,但里面还用 requests.get(),结果压测发现 QPS 比 Flask 还低——因为 requests 是同步阻塞库,会把协程拖回单线程模型。
- 正确做法:用
httpx.AsyncClient(推荐)或aiohttp.ClientSession,并确保每个调用都加await - 别直接
await httpx.get(...),应复用 client 实例,避免反复创建连接池开销 - 若必须调用遗留同步函数(如
hashlib.sha256()),用await asyncio.to_thread()包一层,别让它在主线程跑
中间件里读 request.body() 会破坏后续解析
很多人在鉴权中间件里写 body = await request.body(),结果下游路由调用 await request.json() 时抛 Request body not found——因为 ASGI 的 request body 流只能被消费一次。
- 安全做法:读完后把 raw bytes 存进
request.state.raw_body,供后续依赖或路由按需解析 - 别在中间件里直接
json.loads(),CPU 密集操作应交给await asyncio.to_thread(json.loads, body) - 如果只是校验签名或审计日志,用
await request.body()足够;需要结构化数据时,再从request.state取出解析
最容易被忽略的是数据库驱动本身是否异步——用了 async def + httpx 却连 psycopg2(同步)都还在用,等于只换了个壳。SQLAlchemy 2.0+ 的 create_async_engine、asyncpg、motor 才是真异步底座。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











