目录
一.FastAPI表单数据
先检查是否安装mutilpart包,如果没有安装uv pip install python-multipart
python
# 表单数据
# 安装pip install python-multipart==0.0.20
from fastapi import FastAPI, Form
from pydantic import BaseModel
from typing import Annotated
app = FastAPI()
@app.post('/login1')
def login1(username: str = Form(...), password: str = Form(...)):
return {'username': username, 'password': password}
class User1(BaseModel):
username: str
password: str
@app.post('/login2')
def login2(user: Annotated[User1, Form()]):
return user
class User2(BaseModel):
username: str = Form(...)
password: str = Form(...)
@app.post('/login3')
def login3(user: Annotated[User2, Form()]):
return user
if __name__ == '__main__':
import uvicorn
uvicorn.run(app='main11:app', host='127.0.0.1', port=8000, reload=True)
二.FastAPI异步处理
在 FastAPI 中,异步( async )和非异步(同步)编程方式是其核心特性之一,它们在处理请求、性能以及并发能力上有着显著的区别。

异步 (async def)
事件循环 :异步代码运行在 Python 的异步事件循环(如 asyncio )中。事件循环负责协调多个协
程( coroutines ),在某个协程等待 I/O 操作(如数据库查询或 HTTP 请求)时,事件循环可以切
换到其他协程执行。
非阻塞 :当一个异步函数调用 await ,它会暂停执行,将控制权交回事件循环,允许其他任务运 行,直到等待的操作完成。
并发性 :异步模式允许单个线程处理大量并发请求,特别适合高并发场景(如 Web 服务器处理大
量客户端请求)。
非异步 (def)
阻塞式执行 :同步函数在调用时会完全占用线程,直到函数执行完成才会释放线程。
线程池 :在 FastAPI 中,同步函数由工作线程( worker threads )处理, Uvicorn ( FastAPI 常用
的 ASGI 服务器)会将同步函数放入线程池运行。
并发限制 :线程池的大小限制了同步函数的并发能力。如果线程池耗尽(例如,处理大量阻塞请
求),新请求将排队等待。
python
# 测试异步
from fastapi import FastAPI
import asyncio
import time
app = FastAPI()
# 异步 endpoint:模拟并发 I/O 操作
@app.get("/async")
async def async_endpoint():
start = time.time()
# 模拟 5 次异步 I/O 操作(并发执行)
tasks = [asyncio.sleep(1) for _ in range(5)]
await asyncio.gather(*tasks)
end = time.time()
return {"异步时长": f"{end - start:.2f}秒"}
# 同步 endpoint:模拟相同的 I/O 操作
@app.get("/sync")
def sync_endpoint():
start = time.time()
# 模拟 5 次同步 I/O 操作(顺序执行)
for _ in range(5):
time.sleep(1)
end = time.time()
return {"同步时长": f"{end - start:.2f}秒"}
if __name__ == "__main__":
import uvicorn
uvicorn.run('main12:app', host="127.0.0.1", port=8000, reload=True)
异步执行明显时间快很多。
三.文件上传
python
# 文件上传
# pip install aiofiles==24.1.0
from pathlib import Path
from fastapi import FastAPI, File, UploadFile, HTTPException, Form
import aiofiles
app = FastAPI()
# 小文件(10MB以下的文件)用bytes类型接收,不必异步,因为太小
@app.post("/upload1/")
def upload_file1(file: bytes = File(...)):
# write 写入模式,如果这个文件已存在会直接清空覆盖;不存在就新建文件
with open('./data/file.jpg', 'wb') as f:
f.write(file)
return {"msg": "文件上传成功"}
# 大文件(10MB以上的文件)用UploadFile类型接收,
# 会按照块逐步读取文件内容,避免一次性将整个文件加载到内存中
@app.post('/upload2/')
async def upload_file2(file: UploadFile):
# 使用 aiofiles 异步 IO 提升性能;分块读取避免内存溢出
async with aiofiles.open(f'./data/{file.filename}', 'wb') as f:
# chunk = await file.read(1024*1024)
# while chunk:
# await f.write(chunk)
# chunk = await file.read(1024*1024)
# 分块读取文件内容(和上面四行效果一致)
while chunk := await file.read(1024*1024): # 分块读取1MB
await f.write(chunk)
return {"msg": "文件上传成功"}
# 批量上传文件,一次上传多个文件
@app.post('/batch-upload/')
def batch_upload(files: list[UploadFile] = File(...)):
'''批量上传文件'''
return {"count": len(files), "names": [f.filename for f in files]}
# 限制上传格式
ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.gif'}
@app.post('/upload-image/')
def upload_image(file: UploadFile):
'''限制上传格式'''
# 提取文件名后缀,并转为小写
ext = Path(file.filename).suffix.lower()
print('='*20, ext)
if ext not in ALLOWED_EXTENSIONS:
raise HTTPException(400, '不支持的文件扩展名')
# 保存文件的逻辑
return {"msg": "文件上传成功"}
#表单和图片一块上传
@app.post('/submit-form')
def submit_form(uname: str = Form(...),
file: UploadFile = File(...)):
return {"uname": uname, "filename": file.filename}
if __name__ == '__main__':
import uvicorn
uvicorn.run('main13:app', host="127.0.0.1", port=8000, reload=True)
四.FastAPI请求对象Request
python
# Request获取请求信息
from fastapi import FastAPI, Request
app = FastAPI()
@app.get('/client-info')
async def client_info(request: Request):
return {
"请求URL": request.url,
"请求方法": request.method,
"请求IP": request.client.host,
"请求参数": request.query_params,
"请求头": request.headers,
# "请求json": await request.json(),
"请求cookies": request.cookies,
# "请求form": await request.form(),
# "请求files": request.files,
"请求path_params": request.path_params,
}
if __name__ == '__main__':
import uvicorn
uvicorn.run(app='main14:app', host='127.0.0.1', port=8000, reload=True)
请求信息如下:

五.FastAPI响应类型
在企业级应用中,以下响应类型最为常见:
JSON 响应 :用于 RESTful API 的数据交互,占主导地位(如用户信息、订单、配置)。
1
列表响应:用于分页查询或批量数据返回(如商品列表、日志记录)。
2
文件响应:用于报表导出、文件下载(如 CSV 、 PDF )。
字符串响应:用于健康检查或简单状态反馈。
4
HTML 响应 :用于管理后台或简单的 Web 页面。
5
重定向响应:用于认证流程或 URL 迁移。
6
流式响应:用于实时数据传输或大文件处理。
1.JSON格式
JSON 响应( 80%+ 场景)
JSON 是 Web API 中最常见的响应格式, FastAPI 天然支持通过返回Python 字典或 Pydantic 模型自动序列化为 JSON 响应。
python
# 响应数据Json
from typing import Union, TypeVar, Generic
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
id: int
name: str
tags: list[str] = []
@app.get('/items/dict')
async def get_items_dict():
return {"name": "张三", "age": 18}
@app.get('/items/model')
async def get_items_model():
return Item(id=2, name="Iphone")
@app.get('/items/model2', response_model=Item, response_model_exclude_unset=True)
async def get_items_model2():
return Item(id=2, name="Iphone")
# 定义泛型模型
T = TypeVar('T')
class SuccessResponse(BaseModel, Generic[T]):
status: str = 'success'
data: T
class ErrorResponse(BaseModel):
status: str = 'error'
message: str
code: int
@app.get('/items/{item_id}', response_model=Union[SuccessResponse[Item], ErrorResponse])
async def get_items_model3(item_id: int):
if item_id == 1:
# 定义要返回数据
item = Item(id=1, name="Iphone", tags=["red", "black"])
return SuccessResponse[Item](data=item)
else:
return ErrorResponse(message="Item没有找到", code=404)
if __name__ == '__main__':
import uvicorn
uvicorn.run(app='main15:app', host='127.0.0.1', port=8000, reload=True)
企业级技巧:
通过 response_model 实现响应数据校验和 OpenAPI 文档自动生成
使用 response_model_exclude_unset=True 过滤未设置的默认值字段
组合模型: response_model=UnionSuccessResponse\[Item, ErrorResponse]
2.列表格式
列表响应是指 API 返回一组数据的响应,通常以 JSON 格式返回一个数组或包含数组的对象。常见场景包括:
分页查询 :返回数据的一部分(如每页 10 条记录),避免一次性加载所有数据。
批量数据返回 :返回符合条件的全部或部分数据(如所有订单、日志)。
列表响应通常包含:
数据列表 :核心数据(例如商品列表、日志记录)。
分页元数据 :如总记录数、当前页码、每页记录数、总页数等。
状态信息 :如请求状态(成功或失败)。
过滤 / 排序信息 :描述当前返回的数据是如何过滤或排序的
python
# 响应列表
from typing import List, Optional
from fastapi import FastAPI, Query
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
id: int
name: str
price: float
category: str
class Pagination(BaseModel):
total: int
page: int
page_size: int
total_pages: int
class ListResponse(BaseModel):
status: str = "success"
data: List[Item]
pagination: Pagination
DB = [Item(id=i, name=f'Apple {i}', price=100.0*i, category='ipad' if i %
2 == 0 else 'iphone') for i in range(1, 101)]
@app.get('/items1')
async def get_items1():
return ["Apple1", "Apple2", "Apple3"]
@app.get('/items2')
async def get_items2():
return DB
'''
这两种写法是等价的
name: Optional[str] = None
name: str | None = None
'''
# 过滤信息
@app.get('/items3')
async def get_items3(category: Optional[str] = Query(None, description="分类")):
filtered_items = DB
if category:
filtered_items = [item for item in DB if item.category == category]
return filtered_items
#分页
@app.get('/items4')
async def get_items4(
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(10, ge=1, le=100, description="数量"),
category: Optional[str] = Query(None, description="分类")):
# 对数据进行过滤
filtered_items = DB
if category:
filtered_items = [item for item in DB if item.category == category]
# 对数据进行分页
total = len(filtered_items)
# 计算总页数快捷算法
total_pages = (total + page_size - 1) // page_size
start = (page-1)*page_size
end = start + page_size
return filtered_items[start:end]
'''
total//page_size = count
total%page_size = remain if remain > 0 count+1
'''
#按需封装分页信息
@app.get('/items5')
async def get_items5(
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(10, ge=1, le=100, description="数量"),
category: Optional[str] = Query(None, description="分类")):
# 对数据进行过滤
filtered_items = DB
if category:
filtered_items = [item for item in DB if item.category == category]
# 对数据进行分页
total = len(filtered_items)
# 计算总页数
total_pages = (total + page_size - 1) // page_size
start = (page-1)*page_size
end = start + page_size
return ListResponse(data=filtered_items[start:end],
pagination=Pagination(total=total,
page=page,
page_size=page_size,
total_pages=total_pages))
if __name__ == '__main__':
import uvicorn
uvicorn.run(app='main16:app', host='127.0.0.1', port=8000, reload=True)
3.文件格式
文件响应是指 API 在响应客户端请求时,返回一个文件内容的HTTP 响应,通常包含文件的二进制数据或文本数据。
文件响应的关键特点:
内容类型 :通过 Content-Type 头指定文件的 MIME 类型,如 application/pdf ( PDF 文件)、
text/csv ( CSV 文件)、 application/vnd.ms-excel ( Excel 文件)。
文件内容 :响应的 body 包含文件的实际数据,可能是二进制(如 PDF 、图片)或文本(如 CSV 、JSON 文件)。
python
# 文件响应
from fastapi import FastAPI
from fastapi.responses import Response, FileResponse, StreamingResponse
app = FastAPI()
@app.get('/download_file')
async def get_custom_file():
info = b'File Content'
return Response(
content=info,
media_type='text/plain', # 文本内容
# attachment 直接下载
headers={'Content-Disposition': 'attachment;filename="file.txt"'}
)
@app.get('/download_pdf')
async def get_custom_pdf():
path = './data/fastapi文档.pdf'
return FileResponse(
path=path,
media_type='application/pdf', # PDF文件
headers={'Content-Disposition': 'attachment;filename="file.pdf"'}
)
def generate_chunks(file_path: str, chunk_size: int = 1024*1024*10):
with open(file_path, 'rb') as f:
while chunk := f.read(chunk_size):
#当函数执行到 yield 时,会暂停执行并返回一个值给调用者与 return 不同,return 会终止函数,而 yield 只是暂停
yield chunk
# 大文件采用流式响应
@app.get('/download_mp4')
async def get_custom_mp4():
path = './data/AI变声器.mp4'
return StreamingResponse(
content=generate_chunks(path),
media_type='video/mp4', # mp4文件
headers={'Content-Disposition': 'attachment;filename="file.mp4"'}
)
if __name__ == '__main__':
import uvicorn
uvicorn.run('main17:app', host="127.0.0.1", port=8000, reload=True)
4.其他格式
- 字符串响应
- 重定向
- HTML 响应
- 静态文件
python
# 其响应
from fastapi import FastAPI
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
app = FastAPI()
@app.get('/string1')
async def get_string1():
return 'Hello World' # Content-Type: text/plain
@app.get('/string2')
async def get_string2():
return '<html><h1>Hello</h1></html>'
# response_class=HTMLResponse,返回HTML格式的响应
@app.get('/string3', response_class=HTMLResponse)
async def get_string3():
return '<html><h1>Hello</h1></html>'
@app.get('/redirect1')
async def get_redirect1():
return RedirectResponse(url='/string1')
@app.get('/items')
async def get_items(name: str):
return {'name': name}
@app.get('/redirect2')
async def get_redirect2():
return RedirectResponse(url='/items?name=Jack')
# 挂载HTML目录
# templates目录下有个index.html文件,访问http://localhost:8000/html/index.html即可查看
app.mount('/html', StaticFiles(directory='templates', html=True))
# static目录下有个style.css文件,访问http://localhost:8000/static/style.css即可查看
app.mount('/static', StaticFiles(directory='static'))
if __name__ == '__main__':
import uvicorn
uvicorn.run(app='main18:app', host='127.0.0.1', port=8000, reload=True)