FastAPI 零基础入门完整教程(上)
前言
本文带你从零上手 FastAPI,适合掌握基础Python语法、想要快速搭建后端接口的开发者。
读完本文你可以:搭建Web接口、接收各类请求参数、使用自动交互式接口文档、理解FastAPI高性能的底层原理。
前置要求:掌握基础 Python 语法,会使用
pip安装包。
📑 文章目录
- 一、基础概念
- 二、同步阻塞 VS 异步非阻塞(性能核心原理)
- 三、环境准备:虚拟环境 + 依赖安装
- 四、第一个 FastAPI 程序
- 五、接口参数详解
- 六、同步函数 vs 异步 async
- 七、常用基础功能
- 八、常见踩坑记录
- 坑1:启动命令报错
Application object not found - 坑2:POST接口参数放URL,不使用JSON请求体
- 坑3:生产环境开启
--reload - 坑4:异步函数内部使用同步阻塞代码(time.sleep)
- 坑5:路径参数忘记导入Path
- 坑6:查询参数精细化校验忘记导入Query
- 坑7:请求体内使用Path/Query,混淆三类校验函数
- 坑8:混淆自动返回与手动Response
- 坑9:FileResponse 文件路径错误
- 坑10:StreamingResponse 忘记使用生成器
- 坑11:不清楚响应写法优先级
- 坑12:response_model 无法过滤外层自定义JSONResponse
- 坑13:HTTPException 使用return而不是raise
- 坑14:HTTPException 无法被response_model格式化
- 坑1:启动命令报错
- 九、小结
一、基础概念
什么是 FastAPI
FastAPI 是一个基于 Python 的高性能 Web 框架,专门用于快速构建 API 接口服务。
底层依托两大核心库:
- Pydantic:负责数据类型校验、参数解析
- Starlette:高性能异步 Web 底层框架
FastAPI 两大核心亮点
- Pydantic 类型提示与自动验证
依靠类型注解定义数据格式,自动校验前端传入参数,不用手写大量if判断校验逻辑,代码更简洁。
from pydantic import BaseModel
class User(BaseModel):
username: str
password: str
@app.post("/register")
async def register(user: User):
return user
- 自动生成交互式文档
框架内置接口文档页面,启动服务后直接访问,浏览器内就能调试测试接口,不用额外借助Postman。
两个文档地址:
- 交互式在线调试文档:
http://127.0.0.1:8000/docs(Swagger UI,日常开发首选) - 简洁静态文档:
http://127.0.0.1:8000/redoc
二、同步阻塞 VS 异步非阻塞(性能核心原理)
这是FastAPI高性能的关键,我们用两张流程图直观区分两种运行模式。
1. 传统同步框架(Flask 同步模式)
执行特点:串行排队
请求1遇到IO等待(查询数据库、网络请求)时,整个线程卡住。请求2、请求3只能原地等待,必须等请求1全部处理完成,才能开始处理下一个请求。

短板:IO密集场景资源浪费,并发能力弱。
2. FastAPI 异步模式(async/await)
执行特点:IO等待时切换任务
请求1进入IO等待阶段,不会占用线程。框架会立刻调度请求2开始执行;请求2遇到IO等待,继续调度请求3。
当某个请求的IO操作返回结果,再回头继续完成剩余逻辑。
优势:充分利用等待时间,IO密集场景并发能力大幅提升,性能接近Go、Node.js。
⚠️ 重要提醒:
FastAPI同时兼容同步函数与异步函数。同步函数会放入线程池运行;想要发挥异步性能,数据库、Redis、网络请求需要使用异步客户端。
三、环境准备:虚拟环境 + 依赖安装
3.1 为什么要创建Python虚拟环境
- 依赖版本隔离:不同项目可能需要同一个包的不同版本,全局环境会产生版本冲突;
- 环境干净可控:项目仅安装当前业务需要的库,不会混杂全局其他项目的依赖;
- 方便项目迁移部署:可以导出依赖清单
requirements.txt,其他电脑一键复现环境; - 不污染系统Python:避免误修改系统自带Python的包,防止系统工具异常。
3.2 创建并激活虚拟环境
# 1. 创建虚拟环境,文件夹名称 venv(约定俗成命名)
python -m venv venv
激活命令:
# Windows cmd
venv\Scripts\activate
# Windows PowerShell
venv\Scripts\Activate.ps1
# Mac / Linux
source venv/bin/activate
激活成功后终端前缀会出现 (venv) 标识。
退出虚拟环境命令:
deactivate
3.3 安装项目依赖
虚拟环境激活状态下执行:
pip install fastapi uvicorn
fastapi:框架主体uvicorn:ASGI 异步服务器,运行FastAPI程序
可选:导出依赖清单(后续部署使用)
pip freeze > requirements.txt
四、第一个 FastAPI 程序
4.1 路由基础概念
路由就是 URL 地址和处理函数之间的映射关系,它决定了当用户访问某个特定网址时,服务器应该执行哪段代码来返回结果。
FastAPI 的路由定义基于 Python 的装饰器模式
@app.get("/")
async def root():
return {"message": "Hello World"}
app:FastAPI实例对象@app.get("/"):路由装饰器@:Python装饰器语法标识.get():代表监听 HTTP GET 请求"/":匹配的URL路径
async def root():路径匹配成功后执行的处理函数
4.2 完整示例代码(包含基础路由)
新建文件 main.py
# 导入FastAPI核心类
from fastapi import FastAPI
# 创建FastAPI应用实例,整个项目服务核心载体
app = FastAPI()
# 根路径GET接口
@app.get("/")
async def root():
# FastAPI自动将Python字典序列化为JSON返回
return {"message": "Hello World"}
4.3 逐行代码解析
-
from fastapi import FastAPI
从fastapi框架导入核心类FastAPI,用于创建Web服务应用。 -
app = FastAPI()
实例化app对象,所有路由、接口全部挂载在该实例上。 -
@app.get("/")
路由装饰器:监听 HTTP GET 请求。客户端访问根路径/时,自动执行下方函数。
拓展:
@app.post()、@app.put()、@app.delete()对应不同请求方式。
async def root():
async:定义异步函数,适合数据库读写、网络请求等IO密集场景,FastAPI推荐写法。return {"message": "Hello World"}
FastAPI自动完成JSON序列化,无需手动转换。
4.4 启动服务
uvicorn main:app --reload
参数说明:
main:文件名main.pyapp:代码里app = FastAPI()的实例变量--reload:热重载,修改代码自动重启(仅开发环境使用)
启动成功后访问地址:
- 业务接口地址:http://127.0.0.1:8000
- ✅【重点】交互式调试文档:http://127.0.0.1:8000/docs
- 简洁静态文档:http://127.0.0.1:8000/redoc
五、接口参数详解
5.1 参数基础定义
参数就是客户端发送请求时附带的额外信息和指令。
参数的作用:让同一个接口能根据不同的输入,返回不同的输出,实现动态交互。
通俗理解:同一段接口逻辑,接收不同参数,返回不同数据。例如
/book接口传入book_id=2,服务器返回编号2的图书信息。
5.2 参数三大分类
FastAPI最常用的3类HTTP参数,适用场景区分:
| 参数类型 | 位置 | 典型写法 | 核心用途 | 常用请求方法 |
|---|---|---|---|---|
| 路径参数 | URL路径内部 | /book/{id} | 定位唯一资源 | GET |
| 查询参数 | URL ? 之后 | /search?page=1 | 筛选、分页、排序 | GET |
| 请求体(Body) | HTTP消息体 | JSON格式 | 创建、提交大批量数据 | POST / PUT |
5.3 路径参数(Path Parameter)
路径参数直接写在URL路径中,使用{变量名}标记。
from fastapi import FastAPI
app = FastAPI()
@app.get("/book/{book_id}")
async def get_book(book_id: int):
# 自动校验:访问非数字会直接返回错误
return {"图书编号": book_id, "书名": f"图书{book_id}"}
访问示例:http://127.0.0.1:8000/book/3
适用场景:查询单个、唯一确定的资源详情。
重点解析:book_id: int 类型注解
代码中的 book_id: int 分为两部分:
book_id:参数名称,必须和路由{book_id}保持同名,用于接收URL路径捕获的值;: int:Python类型注解(Type Hint),这是FastAPI实现自动处理的核心。
它带来三大能力:
- ✅ 自动类型转换:URL传递的内容永远是字符串,如
"3",FastAPI自动转换成整数3; - ✅ 自动参数校验:访问
/book/abc,字符串无法转为int,框架直接拦截请求,返回标准422错误,不需要手写if判断; - ✅ 自动生成接口文档:docs页面自动识别参数类型,生成规范的接口说明。
对比演示
# 不带类型注解:book_id 永远是字符串,无自动校验
async def get_book(book_id):
# 添加 :int 注解:自动转换 + 合法性校验
async def get_book(book_id: int)
支持基础类型:str、int、float、bool,FastAPI均可自动解析校验。
进阶:使用 Path() 增加精细化校验
单纯的类型注解只能限制基础数据类型;Path() 可以给路径参数附加额外校验规则、文档描述信息。
需要先从fastapi导入Path。
基础示例:
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/book/{id}")
async def get_book(id: int = Path()):
return {"id": id, "title": f"这是第{id}本书"}
Path() 常用参数一览表
| Path 参数 | 说明 |
|---|---|
... | 代表参数必填(路径参数默认必填) |
gt | greater than,数值大于 |
ge | greater or equal,数值大于等于 |
lt | less than,数值小于 |
le | less or equal,数值小于等于 |
description | 参数描述,自动展示在接口文档中 |
min_length | 字符串最小长度 |
max_length | 字符串最大长度 |
带约束实战示例
# 限制id必须是大于0的整数,并添加文档描述
@app.get("/book/{id}")
async def get_book(
id: int = Path(gt=0, description="图书ID,必须大于0")
):
return {"id": id, "title": f"这是第{id}本书"}
测试效果:
- 访问
/book/5→ 正常返回; - 访问
/book/0→ 触发校验失败,框架自动返回错误;
字符串路径参数长度限制示例:
@app.get("/user/{username}")
async def get_user(
username: str = Path(min_length=3, max_length=20, description="用户名,3~20个字符")
):
return {"username": username}
💡 区分:
普通book_id:int:仅基础类型转换;
book_id:int = Path(...):类型转换 + 自定义校验规则 + 文档注释,项目正式开发推荐使用。
5.4 查询参数(Query Parameter)
URL问号?后面 key=value 形式,多个参数用&分隔。
@app.get("/book/list")
async def list_books(page: int = 1, size: int = 10):
"""page、size带默认值,前端可以不传"""
return {"页码": page, "每页条数": size}
访问示例:http://127.0.0.1:8000/book/list?page=2&size=20
适用场景:列表分页、条件筛选、排序。
默认值与必填参数区分
代码中 page: int = 1, size: int = 10,=1、=10 就是查询参数的默认值。
-
前端不传参数时
访问:http://127.0.0.1:8000/book/list
FastAPI 使用默认值:page=1,size=10 -
前端传参数时
访问:http://127.0.0.1:8000/book/list?page=3&size=20
传入的值会覆盖默认值:page=3,size=20
两种参数定义模式
- 可选参数(存在默认值)
page: int = 1,客户端可以不传参数,自动启用默认值 - 必填查询参数(无默认值)
async def list_books(page: int, size: int = 10):
page 没有默认值,代表必填;请求不带?page=xx,框架直接返回422校验错误。
进阶:使用 Query() 增加精细化校验
普通默认值写法仅支持基础类型;Query() 可以给查询参数附加范围校验、字符串长度限制、接口文档描述,和路径参数的Path()配套对应。
需要先从fastapi导入Query。
基础规范示例:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/book/list")
async def list_books(
page: int = Query(1, ge=1, description="页码,最小为1"),
size: int = Query(10, ge=1, le=100, description="每页条数,范围1~100")
):
return {"页码": page, "每页条数": size}
字符串查询参数长度限制示例:
@app.get("/book/search")
async def search_book(
keyword: str | None = Query(None, min_length=2, max_length=30, description="图书搜索关键词,选填")
):
return {"关键词": keyword}
必填查询参数使用Query(...)写法(...代表必填):
@app.get("/book/filter")
async def filter_book(
category: str = Query(..., description="图书分类,必填参数")
):
return {"分类": category}
💡 统一记忆规律:
路径参数精细化校验 →Path()
查询参数精细化校验 →Query()
请求体参数校验 →Field()(来自pydantic)
5.5 请求体 Body(POST JSON参数)
5.5.1 请求体基础概念
在HTTP协议中,一次完整请求由三部分构成:
- 请求行:请求方法、URL、HTTP协议版本
- 请求头(Headers):元数据信息,例如
Content-Type、Authorization认证头 - 请求体(Body/消息体):真正承载业务数据的载体
请求体特点:
- 位置:HTTP请求消息体内部
- 适用场景:创建资源、更新资源,适合传递大量结构化数据(最常用JSON)
- 常用请求方法:
POST、PUT
⚠️ HTTP规范:GET请求不推荐携带请求体,所有查询筛选数据尽量放在查询参数。
5.5.2 CURL 请求示例解析
curl -X 'POST' \
'http://127.0.0.1:8000/register' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"username": "daqiu",
"password": "123456"
}'
参数拆解:
-X 'POST':指定HTTP请求方式为POST- 地址:目标接口URL
-H:添加请求头Content-Type: application/json关键:告诉后端本次请求体是JSON格式accept: application/json:告知服务器,客户端期望接收JSON格式响应
-d(data):后面跟随的内容就是请求体JSON数据
5.5.3 为什么请求体必须使用 Pydantic BaseModel?
- 自动JSON解析与类型转换
前端传递JSON字符串,FastAPI自动把JSON映射为BaseModel实例,无需手动json.loads()解析。 - 全自动数据校验
自动校验字段类型、长度、数值范围;格式错误直接返回标准422错误,不用手写大量if判断。 - 自动生成接口文档
docs页面自动识别模型字段、描述、约束,生成请求体示例。 - 代码可读性强
集中定义数据结构,复用模型(新增、修改接口可以共用同一个实体类)。 - 内置序列化能力
可以直接将模型对象返回,FastAPI自动序列化为JSON。
补充区分:路径/查询参数直接写在函数参数;请求体JSON数据必须用BaseModel接收,这是FastAPI设计规范。
5.5.4 基础请求体示例
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# 定义接收JSON的模型
class BookCreate(BaseModel):
title: str
price: float
author: str | None = None # 可选字段
@app.post("/book")
async def create_book(book: BookCreate):
return {"msg": "图书创建成功", "data": book}
前端请求JSON示例
{
"title": "FastAPI入门教程",
"price": 59.9
}
5.5.5 进阶:Pydantic Field() 精细化校验
Path()、Query()用于URL参数;请求体模型内部字段约束使用 Field(),需要从pydantic导入。
from pydantic import BaseModel, Field
class User(BaseModel):
# ... 代表必填字段
username: str = Field(..., min_length=3, max_length=20, description="用户名,3~20字符")
password: str = Field(..., min_length=6, max_length=32, description="密码,最少6位")
# 设置默认值,可选字段
age: int = Field(default=18, ge=0, le=120, description="年龄,0~120,默认18岁")
Field() 常用参数对照表
| Field 参数 | 含义 |
|---|---|
... | 字段必填,没有默认值 |
default | 设置字段默认值 |
gt / ge | 数值大于 / 大于等于 |
lt / le | 数值小于 / 小于等于 |
min_length / max_length | 字符串最小/最大长度 |
description | 字段说明,自动展示在接口文档 |
完整可运行接口示例:
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class UserRegister(BaseModel):
username: str = Field(..., min_length=3, max_length=20, description="注册用户名,3~20位字符")
password: str = Field(..., min_length=6, description="登录密码,至少6位")
age: int | None = Field(None, ge=1, le=150, description="年龄,选填,范围1~150")
@app.post("/register")
async def register(user: UserRegister):
return {"message": "注册成功", "user_data": user}
5.6 响应类型(Response)
5.6.1 基础原理
默认情况下,FastAPI 会自动将路径函数返回的 Python 对象(字典、列表、Pydantic模型),通过内置编码器转为JSON格式,自动包装成 JSONResponse 返回。
@app.get("/")
async def root():
# 直接返回字典,FastAPI自动序列化为JSON
return {"message": "hello world"}
优势:不需要手动调用 json.dumps(),开发者专注业务逻辑。
当需要返回 非JSON数据(HTML页面、纯文本、文件、数据流、跳转链接)时,就需要手动导入并使用各类响应类。
5.6.2 全部响应类型对照表
| 响应类型 | 用途 |
|---|---|
| JSONResponse | 默认响应,前后端分离接口标准,返回JSON数据 |
| HTMLResponse | 返回HTML页面内容,用于服务端渲染网页 |
| PlainTextResponse | 返回纯文本字符串 |
| FileResponse | 返回本地文件,触发浏览器下载/预览 |
| StreamingResponse | 流式输出(大文件分片、AI对话实时打字效果) |
| RedirectResponse | 请求重定向,跳转到另一个URL |
导入方式:所有响应对象统一从
fastapi.responses导入
from fastapi.responses import (
JSONResponse,
HTMLResponse,
PlainTextResponse,
FileResponse,
StreamingResponse,
RedirectResponse
)
5.6.3 响应类型两种设置方式
FastAPI 提供两种写法实现自定义响应,适用场景明确区分:
方式1:装饰器指定 response_class
适用场景:接口固定只返回同一种类型(HTML、纯文本),写法简洁,直接返回原始字符串,无需手动实例化响应对象。
@app.get("/html", response_class=HTMLResponse)
async def get_html():
# 直接返回字符串,FastAPI自动包装为HTMLResponse
return "<h1>这是标题</h1>"
✅ 优点:代码简洁,自动填充对应Content-Type
⚠️ 限制:接口只能使用单一响应类型,无法动态切换返回对象
方式2:函数内部 return 响应对象
适用场景:文件下载、图片、流式响应、需要动态切换响应类型、自定义文件名/响应头的场景
@app.get("/file")
async def get_file():
file_path = "./files/1.jpeg"
# 手动实例响应对象并返回
return FileResponse(file_path)
✅ 优点:高度灵活,支持分支逻辑动态返回不同响应、传入专属参数(下载文件名、缓存策略等)
⚠️ 优先级规则:
如果同时配置response_class并且函数返回响应对象,以return的响应对象为准,装饰器参数失效。
@app.get("/demo", response_class=HTMLResponse)
async def demo():
# 最终返回文件,response_class配置不生效
return FileResponse("test.jpg")
📌 最佳实践建议
- 静态网页、纯文本接口 → 使用
response_class装饰器写法- 文件下载、流式输出、重定向、动态多分支接口 → 直接return响应对象
5.6.4 response_model:自定义约束响应输出格式
核心概念
response_model 是路由装饰器(@app.get/@app.post)的关键参数,接收一个 Pydantic模型,用来严格约束API最终输出JSON结构。
四大核心作用:
- 自动数据校验:过滤非法字段、校验数据类型;
- 自动序列化:数据库ORM对象、字典自动转为符合模型规范的JSON;
- 数据安全:自动剔除敏感字段(密码、密钥),防止隐私泄露;
- 自动生成接口文档:
/docs页面展示标准返回结构。
完整示例代码
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# 定义响应规范模型
class News(BaseModel):
id: int
title: str
content: str
# 通过 response_model 指定输出格式
@app.get("/news/{id}", response_model=News)
async def get_news(id: int):
# 返回原始字典,FastAPI自动按照News模型过滤、格式化输出
return {
"id": id,
"title": f"这是第{id}本书",
"content": "这是一本好书",
"secret": "数据库内部密钥" # 额外字段会被自动丢弃,不会返回前端
}
重点特性:函数返回内容里超出模型定义的字段会被自动移除,天然防止敏感信息泄露。
拓展常用配置
- 返回列表数据:
response_model=List[News]
from typing import List
@app.get("/news", response_model=List[News])
async def list_news():
return [
{"id":1,"title":"标题1","content":"内容1"},
{"id":2,"title":"标题2","content":"内容2"}
]
- 可选参数、默认值、字段别名均可在Pydantic模型内定义,响应输出自动遵守规则。
🔔 区分易混淆概念
response_class:控制HTTP响应载体类型(JSON/HTML/文件/文本),修改Content-Type;response_model:只针对JSON响应,约束返回JSON里面有哪些字段、类型是什么,不改变响应载体。
可以同时使用:
@app.get("/news/{id}", response_model=News, response_class=JSONResponse)
5.6.5 HTTPException 异常响应处理
核心概念
对于客户端引发的业务错误(4xx状态码),例如资源不存在、认证失败、参数非法,推荐使用 fastapi.HTTPException,通过 raise 主动抛出异常,中断接口正常流程,返回标准格式错误响应。
适用场景:业务分支判断不满足条件,主动告知客户端请求错误。
完整可运行示例
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/news/{id}")
async def get_news(id: int):
id_list = [1, 2, 3, 4, 5, 6]
if id not in id_list:
# 抛出异常,终止执行,返回标准错误
raise HTTPException(status_code=404, detail="当前id不存在")
return {"id": id}
客户端访问 /news/100 收到响应:
{"detail":"当前id不存在"}
参数说明
status_code:HTTP标准状态码(404资源不存在、401未认证、403权限不足、400请求错误)detail:给前端可读的错误信息headers:可选,自定义异常响应头(常用于鉴权场景)
raise HTTPException(
status_code=401,
detail="token失效",
headers={"WWW-Authenticate": "Bearer"}
)
⚠️ 重要区分
raise HTTPException:主动抛出业务客户端错误(4xx);- Python原生
Exception:服务端未知异常(500),框架默认捕获返回500; - 不要使用
return JSONResponse代替HTTPException:异常可以被全局异常处理器统一捕获格式化。
拓展:结合response_model同时使用
@app.get("/news/{id}", response_model=News)
async def get_news(id: int):
if id > 100:
raise HTTPException(status_code=404, detail="新闻不存在")
return {"id": id, "title":"标题","content":"内容"}
5.6.6 每种响应完整可运行示例
① JSONResponse(默认方式)
两种写法等价:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
# 写法1(推荐简写,自动封装JSONResponse)
@app.get("/json1")
async def json_demo1():
return {"code": 200, "msg": "成功"}
# 写法2 手动构造JSONResponse(适合自定义状态码、响应头)
@app.get("/json2")
async def json_demo2():
return JSONResponse(
content={"code": 200, "msg": "手动JSON响应"},
status_code=200
)
② HTMLResponse 返回网页
@app.get("/html", response_class=HTMLResponse)
async def html_page():
html_content = """
<html>
<head><title>测试页面</title></head>
<body>
<h1>Hello FastAPI HTML</h1>
</body>
</html>
"""
return html_content
③ PlainTextResponse 纯文本
@app.get("/text", response_class=PlainTextResponse)
async def text_demo():
return "这是一段纯文本内容,不是JSON"
④ RedirectResponse 重定向跳转
@app.get("/redirect")
async def redirect_demo():
# 访问接口自动跳转到百度
return RedirectResponse(url="https://www.baidu.com")
⑤ FileResponse 文件下载/预览
@app.get("/download")
async def download_file():
# 参数填写本地文件路径
return FileResponse(
path="test.txt",
filename="下载后的文件名.txt" # 浏览器下载显示的文件名
)
⑥ StreamingResponse 流式响应(重点!AI问答常用)
适用于:超大文件分片传输、大模型打字机实时输出
import asyncio
from fastapi.responses import StreamingResponse
# 异步生成器,不断产生文本
async def stream_generator():
words = ["你", "好", "!", "这", "是", "流", "式", "输", "出"]
for w in words:
yield w
await asyncio.sleep(0.2)
@app.get("/stream")
async def stream_demo():
return StreamingResponse(stream_generator(), media_type="text/plain")
5.6.7 补充关键知识点
知识点:自定义响应头、状态码
手动使用Response对象时,可以自由设置HTTP响应头:
@app.get("/header-demo")
async def header_test():
return JSONResponse(
content={"msg": "带自定义头部"},
headers={"Token": "abc123456", "X-Name": "fastapi"}
)
5.6.8 使用场景选择指南
- 前后端分离接口 → JSONResponse(默认)
- 需要规范返回字段、屏蔽敏感信息 → 搭配 response_model
- 客户端业务错误主动抛出 → HTTPException
- 需要返回网页页面 → HTMLResponse
- 返回简单文本、日志内容 → PlainTextResponse
- 文件下载、图片预览 → FileResponse
- AI流式对话、超大文件边读边传 → StreamingResponse
- 登录成功跳转、旧链接迁移 → RedirectResponse
5.6.9 常见误区
❌ 误区1:混用响应类型
不要同时返回 FileResponse 又返回字典,一次接口只能返回一个响应对象。
❌ 误区2:GET请求试图返回大文件不用流式
超大文件直接使用FileResponse会一次性加载到内存,极限场景推荐StreamingResponse分片读取。
❌ 误区3:分不清 response_class 和 response_model
response_class 修改HTTP载体;response_model 约束JSON内部字段,仅对JSON响应生效。
❌ 误区4:以为response_model会修改入参
response_model只管控输出;请求入参校验使用 pydantic模型作为函数参数。
❌ 误区5:混淆 HTTPException 和 return JSONResponse
客户端业务错误优先raise HTTPException,方便全局异常拦截统一格式化;不要手动return错误JSON。
六、同步函数 vs 异步 async
两种写法FastAPI都原生支持:
# 异步写法(推荐IO密集场景:数据库、网络请求)
@app.get("/async-demo")
async def async_demo():
return {"type": "async"}
# 普通同步写法
@app.get("/sync-demo")
def sync_demo():
return {"type": "sync"}
七、常用基础功能
1. 接口标签,文档分组
@app.get("/book/{book_id}", tags=["图书模块"])
async def get_book(book_id: int):
return {"图书编号": book_id}
文档页面会按照标签分组,方便管理大量接口。
2. 自定义响应状态码
from fastapi import status
@app.post("/book", status_code=status.HTTP_201_CREATED)
async def create_book():
return {"msg": "创建成功"}
八、常见踩坑记录
坑1:启动命令报错 Application object not found
原因:uvicorn 文件:实例名 名称不匹配
解决:确认 app = FastAPI() 的变量名和启动命令保持一致
坑2:POST接口参数放URL,不使用JSON请求体
现象:接口提示请求体缺失
解决:POST JSON参数需要在Body传递,不要放在查询参数
坑3:生产环境开启 --reload
风险:性能下降、存在安全隐患
解决:线上部署移除 --reload 参数
坑4:异步函数内部使用同步阻塞代码(time.sleep)
会阻塞事件循环,破坏异步并发能力;推荐使用asyncio.sleep()
坑5:路径参数忘记导入Path
报错提示找不到Path;解决:from fastapi import Path
坑6:查询参数精细化校验忘记导入Query
报错提示找不到Query;解决:from fastapi import Query
坑7:请求体内使用Path/Query,混淆三类校验函数
错误示范:模型字段内部写Query()
✅正确:请求体模型内部约束使用Field()
区分口诀:URL参数用Path/Query,模型内部字段约束用Field
坑8:混淆自动返回与手动Response
直接return字典会自动变成JSON;一旦手动构造HTMLResponse/FileResponse,就不能再返回字典。
坑9:FileResponse 文件路径错误
相对路径容易找不到文件,正式项目建议使用绝对路径。
坑10:StreamingResponse 忘记使用生成器
必须传入生成器函数(yield),直接传入完整字符串无法实现流式输出。
坑11:不清楚响应写法优先级
response_class 仅在函数返回原生字符串/字典时生效;如果return了响应实例,装饰器配置直接失效。
坑12:response_model 无法过滤外层自定义JSONResponse
如果手动 return JSONResponse(content=data),response_model 不会生效,需要直接返回原始数据交由框架序列化。
坑13:HTTPException 使用return而不是raise
必须使用 raise HTTPException(),return无法中断流程,不会触发标准异常处理。
坑14:HTTPException 无法被response_model格式化
response_model只处理正常成功返回;异常响应需要全局异常处理器统一封装。
九、小结
- FastAPI依靠类型注解 + Pydantic自动完成参数校验,减少重复代码;
- 内置交互式API文档,访问地址
http://127.0.0.1:8000/docs,极大提升前后端协作效率; - 异步非阻塞机制让IO密集场景并发性能远超传统同步Web框架;
- 同步、异步写法同时兼容,开发使用
uvicorn --reload,生产环境建议搭配进程管理工具; - 开发项目优先创建独立虚拟环境,实现依赖隔离,避免版本冲突;
- 路由本质是URL与处理函数的映射关系,FastAPI通过装饰器语法快速定义路由。
- 参数分为路径参数、查询参数、请求体三类,不同场景选择合适的传参方式。
- 路径参数进阶:使用
Path()可以实现数值范围、字符串长度等精细化自定义校验,同时完善接口文档。 - 查询参数进阶:使用
Query()实现分页、筛选参数的约束校验,支持设置默认值、必填、长度限制、数值范围,自动同步到接口文档。 - 请求体进阶:POST/PUT传递JSON依靠
BaseModel接收;模型字段约束使用Field();牢记区分:Path/Query处理URL参数,Field处理请求体模型字段。 - 响应支持多种载体(JSON/HTML/文件/流),区分
response_class与response_model;客户端业务错误优先抛出HTTPException。
参考资料:

&spm=1001.2101.3001.5002&articleId=163301401&d=1&t=3&u=3761314929f14c09b8e09c684ead3607)
1万+

被折叠的 条评论
为什么被折叠?



