⚡ FastAPI 暴击入门:三行代码,让你的接口快到起飞
别被"后端框架"四个字吓到——FastAPI 可能是你写过最爽的接口框架。这篇不是官方文档的搬运,而是把知识库里啃过的干货,按"先跑起来再讲原理"的顺序重新炖了一遍。每一段代码都能直接复制运行,版本也对得上。我会盯着,别慌。
📑 目录
- FastAPI 到底是什么
- 三步跑起第一个接口
- 自动文档:/docs 与 /redoc
- 路由与参数:路径/查询/请求体
- Pydantic 模型与响应过滤
- APIRouter:把路由拆干净
- 依赖注入 Depends
- 异步编程:快在哪
- 数据库 & ORM 入门
- 自检清单
1. FastAPI 到底是什么
一句话:FastAPI 是一个用 Python 写接口的"快"框架——开发快、运行快、写起来还不容易出错。它站在两个巨人肩膀上:
- Starlette:提供 ASGI 异步底层和 Web 能力(路由、请求、WebSocket 等)。
- Pydantic v2:负责数据校验与序列化,性能好、类型提示友好。
它凭什么"快到飞起"?三个核心卖点:
| 能力 | 说明 | 对你意味着什么 |
|---|---|---|
| 极快的性能 | 基于 ASGI,性能对标 Node / Go 同档 | 高并发接口不虚 |
| 自动类型校验 | 用 Python 类型提示自动校验入参 | 少写一堆 if 判断 |
| 自动交互文档 | 开箱即用 Swagger UI + ReDoc | 不用手写接口文档 |
| 异步原生 | async def一等公民 | I/O 密集场景爽歪歪 |
| 编辑器友好 | 类型提示带来自动补全 | 少查文档,效率拉满 |
💡先记住一个概念:ASGI
WSGI 是老一代(同步)Python Web 接口,ASGI 是新一代(支持异步)接口。FastAPI 是 ASGI 应用,所以要搭配支持 ASGI 的服务器(uvicorn或hypercorn)来跑,不能直接用老式python main.py启动。
2. 三步跑起第一个接口
第 1 步:装包。官方推荐装[standard]额外组,它顺带把uvicorn(服务器)、python-multipart(表单)、pydantic-settings等常用依赖一并带上。
# 建议先建虚拟环境(venv / conda 都行),再装pipinstall"fastapi[standard]"# 装完后确认版本pip show fastapi|findstr"Version"# Windows# 本文基准:fastapi 0.141.x,pydantic 2.x第 2 步:写应用。新建main.py,三行核心代码就能跑:
fromfastapiimportFastAPI app=FastAPI()# ① 创建应用实例@app.get("/")# ② 绑定 GET 路由defread_root():return{"msg":"Hello, FastAPI!"}# ③ 返回字典,自动转 JSON第 3 步:启动服务器。用uvicorn启动,main:app表示"main.py 里的 app 变量",--reload让改代码后自动重启(仅开发用)。
uvicorn main:app--reload# 看到类似输出就成功了:# Uvicorn running on http://127.0.0.1:8000# 打开浏览器访问 http://127.0.0.1:8000 → {"msg":"Hello, FastAPI!"}⚠️端口被占用?
若提示端口被占用,换一个即可:uvicorn main:app --reload --port 8080。浏览器访问http://127.0.0.1:8080。注意uvicorn默认只监听本机 127.0.0.1,想让同局域网其他人访问需要加--host 0.0.0.0(生产环境请配合反向代理,勿直接暴露)。
3. 自动文档:/docs 与 /redoc
FastAPI 自动生成两套交互式文档,不用写一行文档代码:
| 地址 | 样式 | 用途 |
|---|---|---|
/docs | Swagger UI | 可在线点按钮发请求、调试接口(最常用) |
/redoc | ReDoc | 排版更顺眼、适合阅读整份 API 说明 |
启动后直接浏览器打开 http://127.0.0.1:8000/docs 。每个接口的参数、返回结构都自动列好,还能直接"Try it out"填参发送。这是 FastAPI 最爽的点之一——文档即代码,永远不会和实现对不上。
🧠深挖:文档从哪来?
FastAPI 在启动时扫描你写的路由、参数类型、Pydantic 模型,自动生成一份OpenAPI 规范(JSON),挂在/openapi.json。Swagger 和 ReDoc 只是把这份 JSON 渲染成不同界面。所以你的类型提示写得越准,文档越准。
4. 路由与参数:路径/查询/请求体
一个接口通常要接收三类"输入",FastAPI 用不同写法区分,且自动帮你做类型转换和校验。
① 路径参数(Path Parameter)
写在{}里,是 URL 路径的一部分,常用于"查某个具体资源"。
@app.get("/items/{item_id}")defread_item(item_id:int):# 声明 int,FastAPI 自动转类型+校验return{"item_id":item_id}请求GET /items/42→{"item_id": 42}。若你传/items/abc,FastAPI 直接返回422 校验错误,根本不会进函数——这就是类型提示的威力。
② 查询参数(Query Parameter)
URL 里?后面那一串,不是路径参数、且给了默认值的就是查询参数。
@app.get("/items/{item_id}")defread_item(item_id:int,q:str|None=None):return{"item_id":item_id,"q":q}# GET /items/42?q=hello → {"item_id":42,"q":"hello"}# GET /items/42 → {"item_id":42,"q":null}③ 请求体(Request Body)
POST 这类"要提交数据"的接口,数据放请求体里,用Pydantic 模型接收(下一节细讲)。
frompydanticimportBaseModelclassItem(BaseModel):name:strprice:floatis_offer:bool=False# 带默认值,可省略@app.post("/items/")defcreate_item(item:Item):# 自动解析 JSON 体 → Item 对象return{"name":item.name,"price":item.price}💡记忆口诀
参数来源怎么判断?路径里有{}的是路径参数;函数参数里没出现在路径、且带默认值的是查询参数;用 Pydantic 模型声明的是请求体。拿不准时去/docs看一眼——界面上写得一清二楚。
5. Pydantic 模型与响应过滤
Pydantic 是 FastAPI 的"数据守门员":进来帮你校验,出去帮你序列化。最容易被忽略、却最有用的是response_model——它能在返回时只挑允许的字段输出,天然防信息泄露。
frompydanticimportBaseModelclassUserIn(BaseModel):# 入参:允许接收密码name:strpassword:strclassUserOut(BaseModel):# 出参:刻意不含 passwordid:intname:str@app.post("/users/",response_model=UserOut)defcreate_user(u:UserIn):# 假设存库后拿到 id=1return{"id":1,"name":u.name,"password":u.password}# ↑ 响应里 password 会被 response_model 自动剥掉!# 实际返回:{"id":1,"name":"xushuai"}🚫安全红线
永远不要让"含密码/令牌/盐"的模型直接作为返回结构。用response_model指定一个精简过的输出模型,是后端防止敏感字段泄露的最低成本防线。这也是面试常问的"如何避免返回用户密码"的标准答法之一。
从数据库对象转 Pydantic,v2 用model_config = ConfigDict(from_attributes=True)(v1 的orm_mode=True已废弃):
frompydanticimportBaseModel,ConfigDictclassProductOut(BaseModel):model_config=ConfigDict(from_attributes=True)# 允许从 ORM 对象读属性id:intname:strprice:float6. APIRouter:把路由拆干净
接口一多,全塞进main.py会爆炸。用APIRouter把同类接口拆成独立文件,最后在main.py收口。
# routers/items.pyfromfastapiimportAPIRouter router=APIRouter(prefix="/items",tags=["items"])# 前缀+分组标签@router.get("/")deflist_items():return[{"id":1,"name":"键盘"}]# main.pyfromfastapiimportFastAPIfromrouters.itemsimportrouterasitems_router app=FastAPI()app.include_router(items_router)# 挂载后路由变为 /items/# 访问 GET /items/ 即生效,/docs 里也会归入 "items" 分组7. 依赖注入 Depends
"依赖注入"听着玄学,其实就是把一段公共逻辑(比如解析分页、校验登录)抽出来,让 FastAPI 在调接口前自动帮你执行并传进来。好处:复用、解耦、好测试。
场景 A:公共分页参数
fromtypingimportAnnotatedfromfastapiimportDependsdefget_pagination(skip:int=0,limit:int=10):return{"skip":skip,"limit":limit}@app.get("/items/")deflist_items(p:Annotated[dict,Depends(get_pagination)]):return{"分页":p,"data":[]}场景 B:登录态校验(实战高频)
用HTTPBearer自动从请求头取Authorization: Bearer <token>,校验失败直接抛 401。
fromfastapiimportDepends,HTTPException,statusfromfastapi.securityimportHTTPBearer,HTTPAuthorizationCredentials security=HTTPBearer()defget_current_user(token:Annotated[HTTPAuthorizationCredentials,Depends(security)]):iftoken.credentials!="secret-token":raiseHTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="无效令牌",)return{"user":"xushuai"}@app.get("/me")defme(user:Annotated[dict,Depends(get_current_user)]):returnuser# 没抛异常才进得来💡为什么用
Annotated[..., Depends(...)]?
这是 FastAPI 推荐的现代写法(类型提示 + 依赖信息放一起,IDE 补全更准)。老写法user: dict = Depends(get_current_user)也能跑,但在复杂场景前者更清晰、更不容易写错。
8. 异步编程:快在哪
FastAPI 支持async def定义异步接口。重点不是"异步一定更快",而是它能在等待 I/O(网络、数据库、文件)时把线程让出来处理别的请求。
# ❌ 同步:串行等待@app.get("/seq")defseq():a=call_api_a()# 等 1sb=call_api_b()# 再等 1sreturn{"a":a,"b":b}# 总共约 2 秒# ✅ 异步:并发等待@app.get("/par")asyncdefpar():a,b=awaitasyncio.gather(call_api_a(),call_api_b())return{"a":a,"b":b}# 总共约 1 秒importasynciofromtypingimportAnnotatedfromfastapiimportFastAPI app=FastAPI()asyncdeffetch_a():awaitasyncio.sleep(1)# 模拟 I/O 等待return"A"asyncdeffetch_b():awaitasyncio.sleep(1)return"B"@app.get("/parallel")asyncdefparallel():a,b=awaitasyncio.gather(fetch_a(),fetch_b())return{"a":a,"b":b}⚠️异步的边界
异步只对I/O 密集型(等网络/数据库/磁盘)有效。若是CPU 密集型(算大数、图像处理),async不会变快,反而可能阻塞事件循环。正确做法:CPU 重活丢给后台任务、线程池(run_in_threadpool)或独立进程/Worker。
9. 数据库 & ORM 入门
真实接口大多要连数据库。FastAPI 官方推荐用SQLAlchemy作 ORM。下一篇博客会深挖 SQLAlchemy,这里先放一个能跑的最小骨架,让你对"接口怎么读到库里的数据"有个整体概念。现代 SQLAlchemy 2.0 风格:用DeclarativeBase+Mapped+mapped_column。
fromsqlalchemy.ormimportDeclarativeBase,Mapped,mapped_columnclassBase(DeclarativeBase):pass# 所有模型继承它classProduct(Base):__tablename__="products"id:Mapped[int]=mapped_column(primary_key=True)name:Mapped[str]=mapped_column()price:Mapped[float]=mapped_column()把数据库会话(Session)通过yield依赖注入给接口,用完自动关:
fromsqlalchemyimportcreate_enginefromsqlalchemy.ormimportsessionmakerfrom.modelsimportBase engine=create_engine("sqlite:///./app.db")# 先用 sqlite 尝鲜SessionLocal=sessionmaker(bind=engine)defget_db():db=SessionLocal()try:yielddb# 把会话交给接口用finally:db.close()# 用完必关,防连接泄漏🧠深挖:同步 vs 异步数据库
上面是同步SQLAlchemy(create_engine+Session),简单直观,适合入门。一旦要扛高并发,应换异步版(create_async_engine+AsyncSession),配合async def接口才能真正发挥异步优势。这部分我们放在 SQLAlchemy 博客里细拆。
自检清单(点开看答案)
Q1:FastAPI 为什么要搭配 uvicorn,而不能 `python main.py` 直接跑?因为 FastAPI 是ASGI应用,不是 WSGI。它需要 ASGI 服务器(uvicorn/hypercorn)来驱动异步事件循环。python main.py只是定义了app对象,没有启动服务器,所以不会监听端口。答法要点:ASGI 异步接口 = 需要 ASGI 服务器。
路径参数写在路由{xxx}里且函数签名声明类型;查询参数是函数里带默认值、没出现在路径的参数。例:@app.get("/items/{i}") def f(i: int, q: str = None)——i是路径参数、q是查询参数。请求/items/abc时,abc转int失败 → 返回422。
它是输出字段白名单:只返回声明在输出模型里的字段,自动把多余字段(如密码、内部字段)剥掉。这是防止敏感信息泄露的低成本的防线,也是从 ORM 对象转 API 输出时的标准做法。
Q4:`Depends` 解决了什么重复劳动?把"每个接口都要做"的公共逻辑(分页解析、登录校验、拿数据库会话等)抽成一个函数,FastAPI 在调接口前自动执行并注入结果。避免在每个路由里复制粘贴同一段代码,也方便统一改逻辑、统一测试。
Q5:异步 `async def` 一定比同步 `def` 快吗?什么时候不该用?不一定。只在I/O 密集型(等网络/数据库/磁盘)时异步才体现优势——等待期间能把线程让给别的请求。遇到CPU 密集型重计算,异步不会更快,还阻塞事件循环,应改用后台任务/线程池/独立进程。
📚 资料来源 & 版本核查
- 本篇内容整理自 Obsidian 知识库
03 - 参考资料/FASTAPI/第 01–07 章及06 - Wiki/FastAPI/卡片。 - 版本基准(已联网核对,2026-08-03):FastAPI 0.141.x(最新稳定线 0.141.1)、Pydantic v2、SQLAlchemy 2.0.x。
- 关键准确性说明:Pydantic v2 用
ConfigDict(from_attributes=True),v1 的orm_mode=True已废弃;SQLAlchemy 2.0 用DeclarativeBase,旧的declarative_base()已不推荐。代码均按 2.x 写法给出,可直接运行。
徐帅 · 代码突击课 · FastAPI 暴击入门 · 基于 Obsidian 知识库整理