astAPI 异步 Web 框架深度解析

1. 背景:为什么需要 FastAPI

1.1 Python Web 框架的两代架构

Python Web 框架经历了两个时代:

时代 规范 代表框架 模型 并发模型
WSGI(Web Server Gateway Interface) PEP 3333 Flask、Django、Tornado 同步请求-响应 多进程 / 多线程
ASGI(Asynchronous Server Gateway Interface) PEP 4843 Starlette、FastAPI、Quart 异步事件驱动 单进程事件循环 + 协程

WSGI 时代的根本矛盾:同步阻塞模型难以承载高并发 I/O。每个请求占用一个线程,线程切换与内存开销大;即使 Flask 内部也有线程池,但整体仍以同步为主,长连接、WebSocket、SSE 支持困难。

ASGI 的出现让 Python 拥有了与 Node.js 同构的单线程事件循环 + 协程 模型:一个进程内可以同时挂起成千上万个 I/O 等待,I/O 期间让出 CPU,事件循环调度其他协程,从而在不增加线程的前提下大幅提升并发吞吐

1.2 FastAPI 是什么

FastAPI 由 Sebastián Ramírez(@tiangolo)于 2018 年发布,是构建在现代 ASGI 框架 Starlette (负责路由、中间件、WebSocket、测试客户端)与 Pydantic (负责数据校验、序列化、JSON Schema 生成)之上的现代、快速(高性能)的 Web 框架

它的三个核心卖点:

  1. 高性能:官方基准测试与 NodeJS、Go 处于同一水平(主要得益于异步与无模板渲染的原生 JSON 序列化)。
  2. 开发效率极高 :基于 Python 类型提示(Type Hints)自动完成参数解析、数据校验、OpenAPI 文档生成(Swagger UI / ReDoc),一套代码同时是文档、校验器与路由声明
  3. 减少缺陷:类型系统 + 自动校验让"参数传错、漏传、类型不对"在请求入口就被拦截,而不是在业务代码深处报错。

1.3 生态定位

复制代码
Python Web 生态全景
├── 全栈大而全:Django(自带 ORM/Admin/Form)
├── 轻量同步:Flask(最小核心,自由组合)
├── 异步极简:Starlette(只有 ASGI 核心能力)
└── 异步 + 类型 + 文档:FastAPI(Starlette 超集 + Pydantic 自动校验)

FastAPI 与 Starlette 是"超集"关系:FastAPI 里能用的路由、中间件、WebSocket、TestClient 均来自 Starlette,FastAPI 在其上叠加了依赖注入系统Pydantic 集成OpenAPI 自动文档


2. 核心 API 说明

2.1 应用实例与路由装饰器

python 复制代码
from fastapi import FastAPI

app = FastAPI(
    title="Demo API",          # OpenAPI 文档标题
    version="1.0.0",
    description="示例服务",
    docs_url="/docs",          # Swagger UI 地址,设为 None 可关闭
    redoc_url="/redoc",        # ReDoc 地址
    openapi_url="/openapi.json",
)

路由装饰器家族:

装饰器 用途
@app.get("/path") GET 请求
@app.post("/path") POST 请求(创建)
@app.put("/path") PUT 请求(整体更新)
@app.patch("/path") PATCH 请求(部分更新)
@app.delete("/path") DELETE 请求
@app.websocket("/ws") WebSocket 连接
@app.api_route("/path", methods="GET", "POST") 多方法

每个装饰器均支持 status_code、response_model、tags、summary、deprecated 等参数,这些参数会直接反映到 OpenAPI 文档中。

2.2 参数声明体系(FastAPI 的灵魂)

FastAPI 通过函数签名声明所有参数,自动完成解析与校验:

python 复制代码
from fastapi import FastAPI, Path, Query, Header, Cookie, Body
from pydantic import BaseModel, Field

class ItemIn(BaseModel):
    name: str = Field(min_length=1, max_length=50)   # 字段级校验
    price: float = Field(gt=0, le=1_000_000)
    tags: list[str] = []

@app.get("/items/{item_id}")
def get_item(
    item_id: int = Path(gt=0),                        # 路径参数 + 校验
    q: str | None = Query(default=None, max_length=50),  # 查询参数
    x_token: str | None = Header(default=None),       # 请求头
    session: str | None = Cookie(default=None),       # Cookie
):
    return {"item_id": item_id, "q": q}

参数来源推断规则:

参数声明位置 来源 说明
函数签名中与路径 {xxx} 同名 路径参数 类型自动转换(str/int/float/bool)
继承自 Pydantic BaseModel 请求体(JSON) 自动校验、嵌套模型支持
Query() 显式声明 查询参数 支持校验、别名、废弃标记
Path() 显式声明 路径参数 同上
Header() / Cookie() 请求头 / Cookie Header 自动转下划线、忽略大小写
Body() 显式声明 请求体 单一标量值入体时需显式声明
File() / UploadFile 文件(multipart/form-data) 文件上传
Form() 表单字段 表单提交
Depends() 依赖注入 见 2.3

推荐写法(现代最佳实践):使用 Annotated 把校验信息与类型放一起,函数签名更干净:

python 复制代码
from typing import Annotated

@app.get("/items/{item_id}")
def get_item(
    item_id: Annotated[int, Path(gt=0)],
    q: Annotated[str | None, Query(max_length=50)] = None,
):
    ...

2.3 响应模型(response_model)

response_model 是 FastAPI 防止"接口字段泄漏"的核心机制:

python 复制代码
class ItemOut(BaseModel):
    id: int
    name: str
    price: float
    # 不包含内部字段 created_by

@app.post("/items", response_model=ItemOut, status_code=201)
def create_item(item: ItemIn):
    return {"id": 1, "name": item.name, "price": item.price, "created_by": "admin"}

特性:

  • 自动过滤:返回 dict 中多余的字段(created_by)不会出现在响应中。
  • 自动序列化:Pydantic 模型、datetime、UUID 等自动转 JSON 兼容类型。
  • 文档联动:OpenAPI 的 response schema 自动生成。
  • 常用参数:response_model_exclude_unset=True(只返回显式设置过的字段)、response_model_exclude={"password"}、response_model_include={...}。

如果返回的是裸 dict 且内含 datetime,FastAPI 不会自动序列化,需要用 fastapi.encoders.jsonable_encoder 手动转换,或直接声明 response_model。

2.4 依赖注入(Depends)

依赖注入是 FastAPI 的杀手锏:把"获取资源"(数据库连接、用户、配置)从业务逻辑中剥离,由框架按需解析

python 复制代码
from fastapi import Depends

# 依赖函数:每个请求调用一次
def get_db():
    db = create_connection()
    try:
        yield db                 # yield 之后的部分在请求结束时执行(清理)
    finally:
        db.close()

def get_current_user(token: str = Header(...)):
    if token != "secret":
        raise HTTPException(status_code=401, detail="未授权")
    return {"username": "admin"}

@app.get("/profile")
def profile(
    db=Depends(get_db),
    user=Depends(get_current_user),
):
    return {"user": user, "db_ok": db is not None}

要点:

  • 依赖可以嵌套:Depends(get_db) 中还能再 Depends 其他依赖。
  • yield 依赖:yield 之前是初始化,之后是清理(无论请求是否异常都会执行 finally),等价于"上下文管理器"。
  • 类依赖:class CommonQueryParams: ... 也可作为 Depends 使用。
  • 默认缓存:同一请求内多次 Depends(same_func) 默认只执行一次(use_cache=True),结果共享;需要每次新建实例时可传 use_cache=False。
  • 全局依赖:FastAPI(dependencies=Depends(verify_api_key)) 或 APIRouter(dependencies=...),对路由组统一生效。

2.5 中间件

python 复制代码
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],   # 或 ["*"]
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

内置常用中间件:

中间件 用途
CORSMiddleware 跨域资源共享(浏览器跨域必备)
GZipMiddleware 响应 gzip 压缩
TrustedHostMiddleware 校验 Host 头防 DNS 重绑定
BaseHTTPMiddleware / @app.middleware("http") 自定义请求/响应处理
python 复制代码
import time
from fastapi import Request

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start = time.perf_counter()
    response = await call_next(request)
    response.headers["X-Process-Time"] = str(time.perf_counter() - start)
    return response

注意:@app.middleware("http") 基于 BaseHTTPMiddleware,性能略低于 ASGI 纯中间件;对性能极端敏感可写原生 ASGI 中间件(call(scope, receive, send))。

2.6 异常处理

python 复制代码
from fastapi import HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

@app.exception_handler(RequestValidationError)
async def validation_handler(request, exc):
    return JSONResponse(status_code=422, content={"detail": exc.errors()})

class BizError(Exception):
    def __init__(self, code: int, msg: str):
        self.code, self.msg = code, msg

@app.exception_handler(BizError)
async def biz_handler(request, exc: BizError):
    return JSONResponse(status_code=200, content={"code": exc.code, "msg": exc.msg})

@app.get("/risk")
def risk():
    raise BizError(10001, "业务校验失败")
  • HTTPException:标准 HTTP 错误,自带 status_code 与 detail。
  • RequestValidationError:参数校验失败默认返回 422,可自定义格式。
  • 自定义异常 + @app.exception_handler:实现业务错误码体系的标准姿势。
  • 注意异常处理器的作用域:路由级 < 应用级,局部优先。

2.7 其他核心 API

能力 API 说明
路由模块化 APIRouter(prefix="/users", tags="用户") + app.include_router(router) 大型项目按业务拆分
后台任务 BackgroundTasks 参数 + tasks.add_task(func, arg) 请求返回后异步执行(轻量)
文件上传 file: UploadFile = File(...) 流式读取,await file.read()
WebSocket @app.websocket("/ws") + await ws.accept()/send_text()/receive_text() 实时双向通信
静态文件 app.mount("/static", StaticFiles(directory="static")) 挂载静态目录
生命周期 lifespan 异步上下文管理器 替代废弃的 on_event("startup")
安全认证 OAuth2PasswordBearer(tokenUrl="/login") 配合 JWT 做登录鉴权
测试 from fastapi.testclient import TestClient 基于 httpx 的同步测试客户端

lifespan 正确写法(新项目必须使用)

python 复制代码
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 启动时:初始化连接池、加载模型
    app.state.pool = await create_pool()
    yield
    # 关闭时:释放资源
    await app.state.pool.close()

app = FastAPI(lifespan=lifespan)

3. 详细使用说明(完整可运行示例)

3.1 工程骨架

复制代码
myapi/
├── main.py            # 应用入口
├── routers/
│   ├── __init__.py
│   ├── items.py       # 商品路由
│   └── users.py       # 用户路由
├── schemas.py         # Pydantic 模型
├── deps.py            # 依赖(get_db/get_current_user)
└── requirements.txt

requirements.txt:

复制代码
fastapi==0.115.* 
uvicorn[standard]==0.30.* 
pydantic==2.* 
sqlalchemy==2.* 
aiosqlite==0.20.* 
httpx==0.27.* # 测试客户端依赖

3.2 最小可运行应用

python 复制代码
# main.py
from fastapi import FastAPI

app = FastAPI(title="最小示例")

@app.get("/")
def root():
    return {"message": "Hello Marvis"}

启动:

复制代码
uvicorn main:app --reload --port 8000
# 浏览器访问 http://127.0.0.1:8000/docs 查看自动生成的 Swagger 文档

3.3 完整 CRUD + 依赖注入 + 异步数据库

python 复制代码
# schemas.py
from pydantic import BaseModel, Field

class ItemCreate(BaseModel):
    name: str = Field(min_length=1, max_length=50)
    price: float = Field(gt=0)

class ItemOut(ItemCreate):
    id: int
python 复制代码
# deps.py
import asyncio
from fastapi import Header, HTTPException

# 演示用的"伪用户"依赖,实际项目替换为 JWT 校验
def get_current_user(x_token: str = Header(...)):
    if x_token != "marvis-secret":
        raise HTTPException(status_code=401, detail="无效令牌")
    return {"username": "marvis"}

async def get_db():
    # 演示连接池,真实项目用 SQLAlchemy async_sessionmaker
    db = {"conn": f"conn-{id(object())}"}
    try:
        yield db
    finally:
        await asyncio.sleep(0)  # 模拟释放
python 复制代码
# routers/items.py
from fastapi import APIRouter, Depends, HTTPException

from deps import get_current_user, get_db

router = APIRouter(prefix="/items", tags=["商品"])

_items: dict[int, dict] = {}
_counter = 0

@router.post("", response_model=dict, status_code=201)
async def create_item(payload: dict, db=Depends(get_db)):
    global _counter
    _counter += 1
    item = {"id": _counter, **payload}
    _items[_counter] = item
    return item

@router.get("/{item_id}")
async def get_item(item_id: int, user=Depends(get_current_user)):
    if item_id not in _items:
        raise HTTPException(status_code=404, detail="商品不存在")
    return _items[item_id]

@router.delete("/{item_id}", status_code=204)
async def delete_item(item_id: int):
    _items.pop(item_id, None)
    return None
python 复制代码
# main.py
from fastapi import FastAPI
from routers import items

app = FastAPI(title="CRUD 示例")
app.include_router(items.router)

3.4 文件上传 + 后台任务

python 复制代码
import shutil
from pathlib import Path
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import FileResponse
from fastapi import BackgroundTasks

UPLOAD_DIR = Path("./uploads")
UPLOAD_DIR.mkdir(exist_ok=True)
app = FastAPI()

def _compress(path: Path):
    """后台任务:模拟耗时压缩"""
    import time
    time.sleep(2)
    print(f"压缩完成: {path.name}")

@app.post("/upload")
async def upload(
    background: BackgroundTasks,
    file: UploadFile = File(...),
):
    dest = UPLOAD_DIR / file.filename
    with dest.open("wb") as f:
        shutil.copyfileobj(file.file, f)   # 流式写入,避免大文件占满内存
    background.add_task(_compress, dest)
    return {"saved": dest.name, "size": dest.stat().st_size}

3.5 WebSocket 回声服务

python 复制代码
from fastapi import WebSocket, WebSocketDisconnect

@app.websocket("/ws/echo")
async def ws_echo(websocket: WebSocket):
    await websocket.accept()
    try:
        while True:
            data = await websocket.receive_text()
            await websocket.send_text(f"echo: {data}")
    except WebSocketDisconnect:
        print("客户端断开")

3.6 测试(pytest + TestClient)

python 复制代码
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_create_item():
    resp = client.post("/items", json={"name": "咖啡", "price": 29.9})
    assert resp.status_code == 201
    assert resp.json()["id"] == 1

def test_auth_required():
    resp = client.get("/items/1")
    assert resp.status_code == 401

4. 常错点 / 坑(20 条高发坑点)

坑 1:async def 里调同步阻塞库,卡死整个事件循环

python 复制代码
# ❌ 错误:requests 是同步阻塞的,在事件循环线程里执行会阻塞所有并发请求
@app.get("/proxy")
async def proxy():
    return requests.get("https://example.com").text

# ✅ 正确:用异步 HTTP 客户端
import httpx

@app.get("/proxy")
async def proxy():
    async with httpx.AsyncClient() as client:
        r = await client.get("https://example.com")
    return r.text

判别规则:I/O 密集用 async def + 异步库;CPU 密集(压缩、加密、图像处理)用普通 def(FastAPI 会自动丢到线程池),或显式 await run_in_threadpool(func, ...)。

坑 2:路由函数用 def 还是 async def 的选择失误

  • 普通 def 路由:FastAPI 放入线程池执行,适合阻塞/CPU 任务,不阻塞事件循环。
  • async def 路由:事件循环直接执行,必须保证函数内没有阻塞调用
  • 误区:认为"想快就必须全部 async"。实际上混合使用没问题------同步路由自动在线程池,两者性能对 IO 场景差异不大,关键是async 路由内绝不能阻塞

坑 3:response_model 缺失导致字段泄漏 / 序列化报错

python 复制代码
class User(BaseModel):
    username: str
    password_hash: str   # 内部字段

# ❌ 直接返回 model 或 dict,password_hash 会泄漏到响应里
@app.get("/user")
def get_user():
    return {"username": "admin", "password_hash": "xxx"}

# ✅ 声明 response_model 自动过滤
@app.get("/user", response_model=UserOut)
def get_user():
    ...

同时 datetime 等类型在裸 dict 中不会自动转字符串,会抛 TypeError: Object of type datetime is not JSON serializable。

坑 4:Pydantic v1/v2 迁移踩坑(API 大改)

v1 v2 说明
@validator("x") @field_validator("x") 校验器装饰器
class Config: orm_mode = True model_config = ConfigDict(from_attributes=True) ORM 模式
obj.dict() obj.model_dump() 转 dict
Model.parse_obj(d) Model.model_validate(d) 从 dict 构建
Model.parse_raw(s) Model.model_validate_json(s) 从 JSON 构建
fields model_fields 字段元信息
SecretStr.get_secret_value() 同左 不变

2023 年 6 月起 FastAPI 0.100+ 默认 Pydantic v2,旧代码迁移务必按表对照修改。

坑 5:Depends 的缓存行为导致"每次请求共享同一对象"

python 复制代码
# 危险模式:依赖返回可变对象且被修改
def get_counter():
    return {"count": 0}

@app.get("/inc")
def inc(c=Depends(get_counter)):
    c["count"] += 1
    return c

默认同一请求内缓存没问题,但跨请求不共享 (每次请求重新解析)。真正的坑在同一请求内多次 Depends 默认只执行一次依赖函数,如果依赖内部有随机数/时间戳且希望每次不同,需 Depends(func, use_cache=False)。

坑 6:路径顺序导致静态路由被动态路由吞掉

复制代码
# ❌ /users/{user_id} 先声明,/users/me 永远匹配不到(user_id="me" 转换失败报 422)
@app.get("/users/{user_id}")
def get_user(user_id: int): ...

@app.get("/users/me")
def get_me(): ...

# ✅ 静态路由放在动态路由之前

坑 7:同步 SQLAlchemy 在 async 路由中使用,阻塞事件循环

python 复制代码
# ❌ 同步 Session 查询是阻塞的
@app.get("/items")
async def list_items():
    return db.query(Item).all()   # 阻塞事件循环

# ✅ 方案 A:路由改普通 def,交给线程池
@app.get("/items")
def list_items():
    return db.query(Item).all()

# ✅ 方案 B:用 SQLAlchemy 2.0 异步扩展 + async_sessionmaker
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
engine = create_async_engine("sqlite+aiosqlite:///./app.db")
Session = async_sessionmaker(engine)

@app.get("/items")
async def list_items(session=Depends(get_async_db)):
    return (await session.execute(select(Item))).scalars().all()

坑 8:请求体解析失败的 422 与 Content-Type

  • POST/PUT 带请求体必须设置 Content-Type: application/json,否则 422。
  • 前端传 application/x-www-form-urlencoded 时需改用 Form() 声明。
  • 客户端 SDK 经常漏配 Content-Type,排查 422 先看请求头。

坑 9:类型提示缺失,OpenAPI 文档与校验失效

python 复制代码
# ❌ 没有类型注解:q 不会被识别为查询参数,文档缺失
@app.get("/search")
def search(q): ...

# ✅ 必须写类型
@app.get("/search")
def search(q: str): ...

FastAPI 的一切魔法都建立在类型提示之上,漏写类型 = 参数变成"黑洞"。

坑 10:可选参数默认值写法错误

python 复制代码
# ❌ Python 3.10+ 语法错误/语义错误
def f(q: str | None): ...           # 无默认值 → FastAPI 视为必填!
def f(q: Optional[str]): ...        # 同上,必填!

# ✅ 正确:必须给默认值 None
def f(q: str | None = None): ...
# 或 Annotated 风格
def f(q: Annotated[str | None, Query()] = None): ...

Pydantic 字段同理:tags: liststr = \[\](注意不要用可变默认值 \[\] 做函数默认参数------Pydantic 会深拷贝,这里安全;但普通函数里仍要避免)。

坑 11:CORS 未配置,浏览器跨域请求被拦截

前端(localhost:3000)调用后端(localhost:8000)报 CORS 错误,但 curl/Postman 正常------不是代码问题,是浏览器同源策略。必须显式配置 CORSMiddleware。注意 allow_origins="\*" 与 allow_credentials=True 不能同时使用(浏览器规范限制)。

坑 12:继续用废弃的 on_event

python 复制代码
# ❌ 已废弃(FastAPI 0.93+ 起推荐 lifespan)
@app.on_event("startup")
async def init(): ...

# ✅ 用 lifespan
@asynccontextmanager
async def lifespan(app):
    app.state.redis = await redis.from_url(...)
    yield
    await app.state.redis.close()

多个 on_event("startup") 的执行顺序不保证,lifespan 单一入口,顺序由代码控制。

坑 13:上传文件不关闭、不限制大小

  • UploadFile 底层是 SpooledTemporaryFile,不关闭会泄漏临时文件句柄。
  • 不限制上传大小可被恶意打满磁盘/内存------流式写入(shutil.copyfileobj)且设置 Content-Length 上限校验。

坑 14:BackgroundTasks 的适用边界被滥用

BackgroundTasks 只是"响应返回后在同一进程里跑个函数",没有重试、持久化、分布式 。邮件通知、简单日志可用;真正的重任务(批量处理、定时任务)应上 Celery / ARQ / RQ。另外 BackgroundTasks 里抛异常不会影响响应,但也不会被记录,需自行 try/except。

坑 15:WebSocket 与 HTTP 的异常处理不同

  • WebSocket 不能用 HTTPException(那是 HTTP 通道的),断线要捕获 WebSocketDisconnect。
  • await websocket.send_* 与 receive_* 不能并发调用,多路推送需自己管理队列。
  • 不 await websocket.accept() 就 send 会抛 RuntimeError。

坑 16:多 worker 下内存状态不一致

复制代码
# --workers 4 时,每个 worker 是独立进程,内存 dict 不共享!
uvicorn main:app --workers 4

用 _items: dict 存数据的 Demo 在单 worker 下没问题,多 worker 部署后请求打到不同进程,数据互相看不见。有状态数据必须落数据库/Redis。另外 --reload 与 --workers 不能同时使用。

坑 17:统一响应包装(code/message/data)的实现误区

python 复制代码
# ❌ 每个路由手写包装,字段名不一致、漏包装
# ✅ 用 response_model 包装 + 业务异常处理器兜底
class ApiResponse(BaseModel):
    code: int = 0
    message: str = "ok"
    data: dict | list | None = None

@app.get("/x", response_model=ApiResponse)
def x():
    return ApiResponse(data={"a": 1})

也可以写一个 @app.middleware 统一包装,但要小心静态文件、健康检查等非 JSON 响应被误包装。

坑 18:jsonable_encoder 与 datetime 序列化

裸 dict 返回 datetime.now() 报错。三种解法:

  1. 声明 response_model(推荐);
  2. jsonable_encoder(data) 手动转换;
  3. 自定义 JSONResponse 的 default=str。

坑 19:依赖里 raise 与 yield 的异常清理顺序

yield 依赖中,如果 yield 之前抛异常,后面的清理代码不会执行;请求处理中抛异常,清理代码(finally)会执行。数据库连接池务必用 try/finally 包住,确保连接归还。

坑 20:测试时事件循环与 TestClient 混用

  • TestClient 是同步的(内部跑一个独立事件循环),在 pytest-asyncio 的 async def 测试里直接调用 client.get 会创建新的事件循环,导致状态不一致------要么全同步测试,要么用 async with AsyncClient(...)。
  • TestClient 依赖 httpx,记得装 httpx,否则 ModuleNotFoundError。

5. 总结:适用场景与选型

5.1 适用场景

场景 推荐度 说明
快速搭建 REST API / 内部工具服务 ★★★★★ 类型 + 文档 + 校验三合一,开发效率极高
机器学习模型推理服务 ★★★★★ 异步吞吐 + Pydantic 校验入参,行业标配
中小型微服务 ★★★★☆ 依赖注入天然适合业务拆分
实时通信(WebSocket/SSE) ★★★★☆ ASGI 原生支持
超大型全栈项目(含模板渲染、复杂 Admin) ★★★☆☆ 模板能力弱于 Django,Admin 需第三方
高 CPU 密集计算服务 ★★☆☆☆ 需要配合线程池/外部计算集群

5.2 与主流框架对比

维度 FastAPI Flask Django Starlette
异步 原生 ASGI 需扩展(Quart) 3.1+ 异步视图 原生
自动 OpenAPI 内置 需 flasgger 需 drf-spectacular
数据校验 Pydantic 自动 手写/序列化库 DRF Serializer 手写
依赖注入 内置 需 flask-injector 需 django-injector
全家桶 无(微框架) 微框架 全栈 微框架
学习曲线 平缓 平缓 平缓

5.3 生产部署要点

复制代码
# 开发
uvicorn main:app --reload

# 生产(单机多进程)
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

# 生产(推荐:反向代理 + 进程管理)
# Nginx → (gunicorn -k uvicorn.workers.UvicornWorker 或 裸 uvicorn) → 应用

生产环境标配:Nginx 反代(TLS 终结)+ uvicorn 多 worker + 超时配置 + 日志收集 + 健康检查端点(/healthz 返回 200)。需要横向扩展时前面加负载均衡,会话状态放 Redis,任务队列上 Celery。


6. FAQ 速查表

问题 答案
如何查看接口文档? 启动后访问 /docs(Swagger)或 /redoc(ReDoc)
为什么我的接口在文档里没有参数? 参数缺类型注解,FastAPI 无法识别
422 是什么错误? 请求参数/请求体验证失败,看响应里的 detail 定位具体字段
如何让某个字段必填? 不设默认值即为必填;Query(..., min_length=1)
如何统一异常返回格式? 自定义异常类 + @app.exception_handler
如何做登录鉴权? OAuth2PasswordBearer + python-jose 签发/校验 JWT
如何连接数据库? SQLAlchemy 2.0 + async_sessionmaker(异步)或同步 Session + def 路由
为什么 curl 正常但浏览器报 CORS? 缺 CORSMiddleware 配置
如何在启动时初始化资源? lifespan 异步上下文管理器
如何处理大文件上传? UploadFile + 流式写入 + 大小限制
后台任务和 Celery 怎么选? 轻量非关键任务用 BackgroundTasks;重任务用 Celery/ARQ
如何做性能压测? locust / wrk,关注 p99 延迟与错误率
版本兼容注意什么? FastAPI 0.100+ 用 Pydantic v2;Python 3.8 以下需 typing.Optional

相关推荐
PiaoKe___3 小时前
云手机原理与 Python 自动化实战:ADB 批量控制、任务调度与落地建议
服务器·arm开发·python·自动化
Gigavision9 小时前
基于BUAA-MIHR数据集的噪声解耦对比学习算法
人工智能·python·深度学习·算法
IPdodo_10 小时前
跨境 API 调用不稳定怎么办:出口、超时重试与链路监控的实践
网络·python·网络协议
Madison-No710 小时前
搭建项目测试环境
linux·运维·服务器·python
计算机编程-吉哥10 小时前
YOLO26 vs YOLO11 vs YOLOv8:深度学习咖啡果实成熟度分割系统【计算机毕业设计选题推荐】
人工智能·python·深度学习·yolo·django·毕业设计
大衛說11 小时前
11 · 异常处理与日志
python
auto_go11 小时前
Python 实战指南(7)——账本动不动就崩?先把“魔法字符串”和“裸报错”干掉
python
Beyond_System|系统之外11 小时前
【学编程】Python基础编程题100道(21-60)
开发语言·python·算法
根目录下的猫12 小时前
RK3588适配的轻量级AI模型推荐
人工智能·后端·python·目标检测