FastAPI实战:待办事项API的增删改查全流程实现

FastAPI实战:待办事项API的增删改查全流程实现

FastAPI作为新一代Python Web框架,以其高性能、自动生成文档和类型提示支持等特性,成为构建现代Web API的首选工具。本文将通过一个完整的待办事项管理项目,深入探讨如何利用FastAPI快速实现路由的增删改查(CRUD)功能,覆盖从项目初始化到API测试的全流程。

一、项目初始化与环境配置

1.1 创建项目结构

使用pip install fastapi uvicorn安装核心依赖后,建议采用如下项目结构:

  1. todo_api/
  2. ├── main.py # 主入口
  3. ├── models.py # 数据模型
  4. ├── schemas.py # 请求/响应模型
  5. ├── crud.py # 数据操作层
  6. └── requirements.txt # 依赖清单

这种分层设计遵循MVC模式,使代码更易于维护和扩展。

1.2 基础应用配置

main.py中初始化FastAPI应用:

  1. from fastapi import FastAPI
  2. from fastapi.middleware.cors import CORSMiddleware
  3. app = FastAPI(title="待办事项API", version="1.0.0")
  4. # 跨域配置(开发环境必备)
  5. app.add_middleware(
  6. CORSMiddleware,
  7. allow_origins=["*"],
  8. allow_methods=["*"],
  9. allow_headers=["*"],
  10. )

二、数据模型设计

2.1 数据库模型(models.py)

使用Pydantic进行数据验证,结合SQLAlchemy(可选)进行持久化:

  1. from pydantic import BaseModel, Field
  2. from typing import Optional
  3. from datetime import datetime
  4. class TodoItem(BaseModel):
  5. id: Optional[int] = Field(default=None, description="自动生成ID")
  6. title: str = Field(..., min_length=3, max_length=50)
  7. description: Optional[str] = Field(default="", max_length=200)
  8. completed: bool = False
  9. created_at: Optional[datetime] = Field(default_factory=datetime.now)
  10. updated_at: Optional[datetime] = Field(default_factory=datetime.now)
  11. class Config:
  12. orm_mode = True # 支持SQLAlchemy模型转换

2.2 请求/响应模型(schemas.py)

分离创建和更新模型,提高接口灵活性:

  1. from pydantic import BaseModel
  2. class TodoCreate(BaseModel):
  3. title: str
  4. description: str = ""
  5. class TodoUpdate(BaseModel):
  6. title: Optional[str] = None
  7. description: Optional[str] = None
  8. completed: Optional[bool] = None

三、CRUD路由实现

3.1 内存存储实现(开发阶段)

使用Python字典模拟数据库:

  1. from typing import Dict, Optional
  2. from fastapi import HTTPException
  3. from models import TodoItem
  4. db: Dict[int, TodoItem] = {}
  5. current_id = 1
  6. def create_todo(item: TodoItem) -> TodoItem:
  7. global current_id
  8. item.id = current_id
  9. db[current_id] = item
  10. current_id += 1
  11. return item
  12. def get_todo(id: int) -> Optional[TodoItem]:
  13. return db.get(id)
  14. def update_todo(id: int, updates: TodoUpdate) -> Optional[TodoItem]:
  15. if id not in db:
  16. raise HTTPException(status_code=404, detail="Item not found")
  17. existing = db[id]
  18. update_data = updates.dict(exclude_unset=True)
  19. updated_item = existing.copy(update=update_data)
  20. db[id] = updated_item
  21. return updated_item
  22. def delete_todo(id: int) -> bool:
  23. if id in db:
  24. del db[id]
  25. return True
  26. return False

3.2 路由定义(main.py扩展)

  1. from fastapi import APIRouter, Path, HTTPException
  2. from schemas import TodoCreate, TodoUpdate
  3. from models import TodoItem
  4. from crud import create_todo, get_todo, update_todo, delete_todo
  5. router = APIRouter(prefix="/todos", tags=["todos"])
  6. @router.post("/", response_model=TodoItem)
  7. def create_todo_item(item: TodoCreate):
  8. """创建新的待办事项"""
  9. # 实际项目需在此转换模型
  10. todo_item = TodoItem(title=item.title, description=item.description)
  11. return create_todo(todo_item)
  12. @router.get("/{id}", response_model=TodoItem)
  13. def read_todo_item(id: int = Path(..., ge=1)):
  14. """获取单个待办事项"""
  15. todo = get_todo(id)
  16. if not todo:
  17. raise HTTPException(status_code=404, detail="Item not found")
  18. return todo
  19. @router.put("/{id}", response_model=TodoItem)
  20. def update_todo_item(id: int, updates: TodoUpdate):
  21. """更新待办事项"""
  22. return update_todo(id, updates)
  23. @router.delete("/{id}")
  24. def delete_todo_item(id: int):
  25. """删除待办事项"""
  26. if not delete_todo(id):
  27. raise HTTPException(status_code=404, detail="Item not found")
  28. return {"message": "Item deleted successfully"}
  29. app.include_router(router)

四、高级功能实现

4.1 分页与过滤

  1. from fastapi import Query
  2. from typing import List
  3. @router.get("/", response_model=List[TodoItem])
  4. def read_todos(
  5. skip: int = Query(0, ge=0),
  6. limit: int = Query(10, le=100),
  7. completed: Optional[bool] = None
  8. ):
  9. """分页获取待办事项列表,支持完成状态过滤"""
  10. items = list(db.values())
  11. if completed is not None:
  12. items = [item for item in items if item.completed == completed]
  13. return items[skip : skip + limit]

4.2 依赖注入与认证

  1. from fastapi import Depends, HTTPException
  2. from fastapi.security import APIKeyHeader
  3. API_KEY = "secret-key"
  4. api_key_header = APIKeyHeader(name="X-API-Key")
  5. def get_api_key(api_key: str = Depends(api_key_header)):
  6. if api_key != API_KEY:
  7. raise HTTPException(status_code=403, detail="Invalid API Key")
  8. return api_key
  9. @router.get("/secure/", response_model=List[TodoItem])
  10. def read_secure_todos(api_key: str = Depends(get_api_key)):
  11. """需要API密钥的受保护端点"""
  12. return list(db.values())

五、测试与文档

5.1 自动生成API文档

FastAPI自动生成Swagger UI和ReDoc文档:

  • 访问/docs查看交互式Swagger界面
  • 访问/redoc查看标准化文档

5.2 单元测试示例

  1. from fastapi.testclient import TestClient
  2. from main import app
  3. client = TestClient(app)
  4. def test_create_todo():
  5. response = client.post(
  6. "/todos/",
  7. json={"title": "Test Item", "description": "Test Description"}
  8. )
  9. assert response.status_code == 200
  10. assert "id" in response.json()
  11. def test_read_todo():
  12. # 先创建测试项
  13. create_resp = client.post(
  14. "/todos/",
  15. json={"title": "Read Test", "description": "Test Read"}
  16. )
  17. todo_id = create_resp.json()["id"]
  18. # 读取测试
  19. response = client.get(f"/todos/{todo_id}")
  20. assert response.status_code == 200
  21. assert response.json()["title"] == "Read Test"

六、生产环境部署建议

  1. 持久化存储:替换内存存储为SQLite/PostgreSQL
  2. 异步支持:使用databases库实现异步CRUD
  3. 性能优化
    • 启用Uvicorn的--workers参数
    • 配置适当的超时设置
  4. 监控:集成Prometheus和Grafana
  5. 日志:使用结构化日志(如loguru

七、常见问题解决方案

7.1 跨域问题

确保正确配置CORS中间件,生产环境应限制具体域名:

  1. app.add_middleware(
  2. CORSMiddleware,
  3. allow_origins=["https://yourdomain.com"],
  4. allow_methods=["GET", "POST", "PUT", "DELETE"],
  5. allow_headers=["*"],
  6. )

7.2 数据验证错误处理

FastAPI自动返回422错误,可通过异常处理器自定义:

  1. from fastapi import Request
  2. from fastapi.responses import JSONResponse
  3. from fastapi.exceptions import RequestValidationError
  4. @app.exception_handler(RequestValidationError)
  5. async def validation_exception_handler(request: Request, exc: RequestValidationError):
  6. return JSONResponse(
  7. status_code=422,
  8. content={"detail": exc.errors(), "body": exc.body},
  9. )

八、扩展建议

  1. 添加用户系统:集成OAuth2或JWT认证
  2. 附件支持:实现文件上传功能
  3. 定时任务:使用Celery处理逾期提醒
  4. WebSocket:实现实时状态更新
  5. GraphQL:通过Strawberry添加GraphQL支持

通过本文的完整实现,开发者可以快速掌握FastAPI的核心功能,构建出符合生产标准的Web API。实际项目中,建议结合具体需求进行模块化扩展,并始终遵循RESTful设计原则。FastAPI的自动文档和类型系统将显著提升开发效率和代码质量,是构建现代微服务的理想选择。