☰
FastAPI 实战教程(上):核心机制、项目配置与完整请求链路
2026/10/5 6:59:41 网站建设 项目流程

内容依据 FastAPI 0.142.2 与官方文档整理,采集日期:2026-10-03。

系列导航

  • 上篇:核心机制、项目配置与完整请求链路(本文)
  • 下篇:数据库、JWT、测试部署与热门项目架构拆解

全系列目录

上篇:先把 FastAPI 的运行机制讲清楚

  1. FastAPI、Starlette、Pydantic 与 Uvicorn 的关系
  2. Python 环境、依赖安装、启动命令与接口文档
  3. 路由注册与 Path、Query、Header、Body 参数
  4. Pydantic 输入校验、响应模型与数据边界
  5. 依赖注入、依赖缓存和yield资源清理
  6. async def、普通def与阻塞调用
  7. 异常处理、统一错误结构与 APIRouter 拆分

下篇:把 Demo 扩展成生产项目

  1. 配置管理、数据库 Session、Repository 与事务
  2. JWT 登录、密码安全、中间件与后台任务
  3. Lifespan、自动化测试、Docker 与生产部署
  4. Open WebUI、Langflow、Full Stack FastAPI Template 源码架构拆解
  5. 从三个项目提炼可复用的生产级目录结构

摘要

FastAPI 的“快”不只指运行性能,也来自类型提示驱动的开发方式。本文从一次 HTTP 请求的完整链路出发,讲清路由、参数提取、Pydantic 校验、响应模型、依赖注入、异常处理与 async/await,并给出可直接运行的最小项目。

一、FastAPI 到底是什么

FastAPI 是一个使用现代 Python 类型提示构建 API 的 Web 框架。它的核心并不是某个神奇装饰器,而是把三个成熟组件组合在了一起:

  • Starlette:提供 ASGI、路由、中间件、WebSocket 等 Web 能力;
  • Pydantic:负责数据解析、类型转换、校验与 Schema;
  • Uvicorn:作为 ASGI Server 接收和转发网络请求。

开发者写下一个函数签名,FastAPI 会同时从中获得参数来源、类型约束、运行时校验规则和 OpenAPI 文档。这就是它“声明一次,获得多种能力”的关键。

一次请求大致经过:

客户端 → Uvicorn/ASGI → 中间件 → 路由匹配 → 依赖解析 → Pydantic 校验 → 路径函数 → 响应模型 → JSON

二、安装与第一个接口

Python 版本建议使用 3.10 或更高。推荐在虚拟环境中安装标准依赖:

python-m venv.venv.venv\Scripts\Activate.ps1 python-m pip install--upgrade pip pip install"fastapi[standard]"

使用 uv 时:

uv init fastapi-democdfastapi-demo uvadd"fastapi[standard]"

新建main.py:

fromfastapiimportFastAPI app=FastAPI(title="FastAPI Demo",version="1.0.0")@app.get("/")asyncdefroot():return{"message":"Hello FastAPI"}

开发模式启动:

fastapi dev main.py

启动后可以访问:

  • 接口:http://127.0.0.1:8000/
  • Swagger UI:http://127.0.0.1:8000/docs
  • ReDoc:http://127.0.0.1:8000/redoc
  • OpenAPI JSON:http://127.0.0.1:8000/openapi.json

fastapi dev会启用热重载,适合本地开发;生产环境应使用fastapi run或显式配置 Uvicorn,不要开启 reload。

【截图占位】补充 Swagger UI 首页和/接口调用结果。

三、路由:URL 和函数如何关联

fromfastapiimportFastAPI,status app=FastAPI()@app.get("/users/{user_id}",tags=["users"])asyncdefget_user(user_id:int):return{"id":user_id}@app.post("/users",status_code=status.HTTP_201_CREATED,tags=["users"],)asyncdefcreate_user():return{"created":True}

@app.get()、@app.post()被称为路径操作装饰器。它们登记了:

  • HTTP 方法;
  • URL 模板;
  • 状态码;
  • 标签与文档元数据;
  • 最终要执行的 Python 函数。

静态路由应放在动态路由之前:

@app.get("/users/me")asyncdefread_current_user():return{"id":"me"}@app.get("/users/{user_id}")asyncdefread_user(user_id:str):return{"id":user_id}

否则/users/me可能先匹配到{user_id}。

四、FastAPI 如何判断参数来自哪里

FastAPI 会结合路径模板、类型和显式标记判断数据来源。

fromtypingimportAnnotatedfromfastapiimportBody,Header,QueryfrompydanticimportBaseModel,FieldclassItemCreate(BaseModel):name:str=Field(min_length=2,max_length=50)price:float=Field(gt=0)description:str|None=None@app.post("/shops/{shop_id}/items")asyncdefcreate_item(shop_id:int,item:ItemCreate,limit:Annotated[int,Query(ge=1,le=100)]=20,user_agent:Annotated[str|None,Header()]=None,source:Annotated[str,Body(embed=True)]="web",):return{"shop_id":shop_id,"item":item,"limit":limit,"user_agent":user_agent,"source":source,}

这里的来源分别是:

参数来源判断依据
shop_idPath出现在/shops/{shop_id}中
limitQuery普通标量且使用Query
user_agentHeader显式使用Header
itemJSON BodyPydantic 模型
sourceJSON Body显式使用Body

如果客户端把shop_id传成无法转换的字符串,或者价格小于等于 0,请求会在进入函数之前返回 422。业务代码因此不用重复写大量类型判断。

五、Pydantic 模型:输入校验不等于数据库模型

frompydanticimportBaseModel,ConfigDict,EmailStr,FieldclassUserCreate(BaseModel):email:EmailStr password:str=Field(min_length=8,max_length=128)nickname:str=Field(min_length=2,max_length=30)classUserPublic(BaseModel):model_config=ConfigDict(from_attributes=True)id:intemail:EmailStr nickname:str

不要用同一个模型同时承担创建参数、数据库实体和公开响应。上面的拆分可以避免把密码字段意外返回给客户端。

response_model不只是文档声明,它还会校验并过滤响应:

@app.post("/users",response_model=UserPublic,status_code=201)asyncdefcreate_user(payload:UserCreate):saved={"id":1,"email":payload.email,"nickname":payload.nickname,"password":payload.password,}returnsaved

尽管saved中含有password,最终响应只会保留UserPublic声明的字段。这是一道非常实用的输出边界。

六、依赖注入:FastAPI 最值得掌握的能力

依赖注入的作用是让路径函数声明“我需要什么”,由框架负责创建、复用和清理。

fromtypingimportAnnotatedfromfastapiimportDepends,Header,HTTPExceptionasyncdefget_token(authorization:Annotated[str|None,Header()]=None,)->str:ifauthorization!="Bearer demo-token":raiseHTTPException(status_code=401,detail="Invalid token")returnauthorization.removeprefix("Bearer ")Token=Annotated[str,Depends(get_token)]@app.get("/profile")asyncdefprofile(token:Token):return{"token":token}

执行/profile时,FastAPI 会先解析get_token需要的 Header,再执行它,最后把返回值注入token。

依赖还可以继续依赖其他依赖:

asyncdefget_current_user(token:Token):return{"id":1,"token":token}CurrentUser=Annotated[dict,Depends(get_current_user)]@app.get("/orders")asyncdeflist_orders(user:CurrentUser):return{"user_id":user["id"],"items":[]}

FastAPI 会构建一棵依赖图,并在单次请求中缓存默认依赖结果。数据库会话、当前用户、权限检查和配置对象都适合用依赖表达。

需要资源清理时使用yield:

asyncdefget_resource():resource=awaitopen_resource()try:yieldresourcefinally:awaitresource.close()

yield之前相当于进入资源,之后相当于退出清理。

七、async def 和 def 应该怎么选

关键不是“异步一定更快”,而是被调用的 I/O 库是否支持await。

@app.get("/async-resource")asyncdefasync_resource():data=awaitasync_http_client.get("https://example.com")returndata.json()

如果第三方库是异步的,使用async def并await。如果使用传统阻塞库,可以写普通def:

@app.get("/sync-report")defsync_report():returnblocking_report_library.build()

FastAPI 会在线程池中执行普通def路径函数,避免直接阻塞事件循环。

最危险的写法是在async def中直接调用耗时的阻塞函数:

@app.get("/bad")asyncdefbad():time.sleep(5)# 阻塞事件循环return{"ok":True}

CPU 密集型计算也不会因为async自动变快。图片处理、模型推理或大规模计算应放进进程池、任务队列或独立服务。

八、异常处理与统一错误结构

fromfastapiimportHTTPException@app.get("/items/{item_id}")asyncdefread_item(item_id:int):ifitem_id!=1:raiseHTTPException(status_code=404,detail={"code":"ITEM_NOT_FOUND","message":"Item not found"},)return{"id":item_id}

大型项目中,可以定义领域异常并集中转换:

fromfastapiimportRequestfromfastapi.responsesimportJSONResponseclassDomainError(Exception):def__init__(self,code:str,message:str):self.code=code self.message=message@app.exception_handler(DomainError)asyncdefhandle_domain_error(request:Request,exc:DomainError):returnJSONResponse(status_code=400,content={"code":exc.code,"message":exc.message},)

业务层只抛领域异常,HTTP 状态码和响应结构由 Web 层统一决定,避免每个路由重复拼装错误。

九、拆分成可维护的项目

当接口超过十几个后,不要继续把所有代码堆在main.py:

app/ ├── main.py ├── api/ │ ├── deps.py │ └── routes/ │ ├── users.py │ └── items.py ├── schemas/ │ ├── user.py │ └── item.py ├── services/ └── core/ └── config.py

路由文件:

fromfastapiimportAPIRouter router=APIRouter(prefix="/items",tags=["items"])@router.get("")asyncdeflist_items():return[]

主应用只负责装配:

fromfastapiimportFastAPIfromapp.api.routesimportitems app=FastAPI()app.include_router(items.router,prefix="/api/v1")

APIRouter 不是为了“目录好看”,而是把路由前缀、标签、依赖和领域边界组合成可独立理解的模块。

十、本期小结

掌握 FastAPI 的关键,不是记住装饰器,而是理解一次请求如何依次完成:

  1. Uvicorn 把网络请求转换成 ASGI 事件;
  2. Starlette 中间件和路由找到路径函数;
  3. FastAPI 解析依赖图和参数来源;
  4. Pydantic 完成转换与校验;
  5. 路径函数执行业务逻辑;
  6. 响应模型过滤输出并生成 JSON;
  7. 同一份类型信息生成 OpenAPI 文档。

掌握上面的请求链路后,就可以进入真实工程:数据库会话、事务、JWT、配置、中间件、测试、后台任务、部署,以及热门开源项目的架构选择。

继续阅读:FastAPI 实战教程(下):数据库、JWT、测试部署与热门项目架构拆解

参考资料

  • FastAPI 官方教程:https://fastapi.tiangolo.com/tutorial/
  • Request Body:https://fastapi.tiangolo.com/tutorial/body/
  • Dependencies:https://fastapi.tiangolo.com/tutorial/dependencies/
  • Async:https://fastapi.tiangolo.com/async/
  • Bigger Applications:https://fastapi.tiangolo.com/tutorial/bigger-applications/
  • PyPI:https://pypi.org/project/fastapi/

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询