作为一名长期使用Python进行Web开发的工程师,我不得不承认FastAPI彻底改变了我的API开发体验。这个基于Starlette和Pydantic的现代框架,完美结合了高性能与开发效率。今天我将分享如何用FastAPI处理各种请求和响应场景,这些技巧都来自我实际项目中的经验总结。
在开始之前,我强烈建议使用虚拟环境隔离项目依赖。这是我常用的初始化流程:
bash复制python -m venv fastapi-env
source fastapi-env/bin/activate # Linux/Mac
fastapi-env\Scripts\activate # Windows
pip install fastapi uvicorn python-multipart
注意:python-multipart是处理表单数据必需的包,很多新手会遗漏这个依赖
创建一个main.py文件,写入以下基础代码:
python复制from fastapi import FastAPI
app = FastAPI(
title="我的API服务",
description="一个演示FastAPI各种功能的项目",
version="0.1.0"
)
@app.get("/")
async def root():
return {"message": "欢迎来到FastAPI世界"}
启动开发服务器:
bash复制uvicorn main:app --reload
FastAPI支持所有标准HTTP方法,使用方式完全一致:
python复制@app.get("/items/")
async def read_items():
return [{"id": 1}]
@app.post("/items/")
async def create_item():
return {"status": "created"}
@app.put("/items/{item_id}")
async def update_item(item_id: int):
return {"item_id": item_id}
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
return {"deleted": item_id}
当路由数量增多时,建议使用APIRouter进行模块化管理:
python复制from fastapi import APIRouter
router = APIRouter(prefix="/api/v1")
@router.get("/users/")
async def get_users():
return ["user1", "user2"]
# 在主应用中挂载
app.include_router(router)
查询参数不仅支持基础类型,还能进行复杂验证:
python复制from fastapi import Query
@app.get("/items/")
async def read_items(
q: str | None = Query(
None,
min_length=3,
max_length=50,
regex="^[a-zA-Z0-9_]*$",
title="查询字符串",
description="用于过滤项目的查询参数"
),
skip: int = Query(0, ge=0),
limit: int = Query(10, le=100)
):
return {"q": q, "skip": skip, "limit": limit}
路径参数同样支持丰富的验证选项:
python复制from fastapi import Path
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(..., title="商品ID", gt=0, le=1000),
q: str | None = None
):
return {"item_id": item_id, "q": q}
Pydantic模型是FastAPI的核心特性之一:
python复制from pydantic import BaseModel, Field
from typing import List, Optional
class Item(BaseModel):
name: str = Field(..., example="Foo")
description: Optional[str] = Field(
None, title="详细描述", max_length=300
)
price: float = Field(..., gt=0)
tags: List[str] = []
@app.post("/items/")
async def create_item(item: Item):
return item
有时需要同时接收多个请求体参数:
python复制class User(BaseModel):
username: str
email: str
class Item(BaseModel):
name: str
price: float
@app.post("/purchase/")
async def purchase(user: User, item: Item, quantity: int = 1):
return {
"user": user,
"item": item,
"quantity": quantity,
"total": item.price * quantity
}
处理HTML表单数据需要使用Form:
python复制from fastapi import Form
@app.post("/login/")
async def login(
username: str = Form(..., min_length=3),
password: str = Form(..., min_length=8)
):
return {"username": username}
文件上传是Web开发的常见需求:
python复制from fastapi import UploadFile, File
@app.post("/upload/")
async def upload_file(
file: UploadFile = File(..., description="要上传的文件")
):
contents = await file.read()
return {
"filename": file.filename,
"size": len(contents),
"content_type": file.content_type
}
可以精确控制API返回的数据结构:
python复制from pydantic import BaseModel
class UserIn(BaseModel):
username: str
password: str
class UserOut(BaseModel):
username: str
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
return user # 密码字段会自动过滤
python复制from fastapi import Response
@app.get("/set-cookie/")
async def set_cookie(response: Response):
response.set_cookie(key="session_id", value="abc123")
response.headers["X-Custom-Header"] = "CustomValue"
return {"message": "Cookie和Header已设置"}
python复制from fastapi import HTTPException
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 0:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "Item ID cannot be zero"}
)
return {"item_id": item_id}
python复制from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
return JSONResponse(
status_code=400,
content={"message": f"值错误: {str(exc)}"}
)
python复制from fastapi import Depends
async def get_db():
# 初始化数据库连接
db = "database_connection"
try:
yield db
finally:
# 关闭连接
pass
@app.get("/items/")
async def read_items(db: str = Depends(get_db)):
return {"db": db}
表单数据无法解析:
Pydantic验证失败:
异步函数阻塞:
以下是一个完整的笔记API实现:
python复制from typing import List, Optional
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field
app = FastAPI()
class Note(BaseModel):
id: int
title: str = Field(..., min_length=1, max_length=50)
content: str
tags: List[str] = []
notes_db = []
@app.post("/notes/", status_code=status.HTTP_201_CREATED)
async def create_note(note: Note):
if any(n.id == note.id for n in notes_db):
raise HTTPException(
status_code=400,
detail="Note ID already exists"
)
notes_db.append(note)
return note
@app.get("/notes/", response_model=List[Note])
async def list_notes(limit: int = 10):
return notes_db[:limit]
@app.get("/notes/{note_id}")
async def get_note(note_id: int):
for note in notes_db:
if note.id == note_id:
return note
raise HTTPException(status_code=404, detail="Note not found")
@app.put("/notes/{note_id}")
async def update_note(note_id: int, updated_note: Note):
for i, note in enumerate(notes_db):
if note.id == note_id:
notes_db[i] = updated_note
return updated_note
raise HTTPException(status_code=404, detail="Note not found")
@app.delete("/notes/{note_id}")
async def delete_note(note_id: int):
for i, note in enumerate(notes_db):
if note.id == note_id:
deleted_note = notes_db.pop(i)
return {"deleted": deleted_note}
raise HTTPException(status_code=404, detail="Note not found")
这个项目展示了FastAPI的核心功能在实际中的应用。我在开发过程中发现,良好的模型设计和合理的路由组织可以显著提高代码的可维护性。