1. FastAPI 第二天:从基础路由到模板渲染实战
刚接触 FastAPI 时,很多人会被它简洁的语法所迷惑,以为两天就能掌握全部精髓。但真正深入使用后才发现,这个看似简单的框架藏着不少值得深挖的细节。第二天学习时,我们该把注意力放在哪些真正影响开发效率的关键特性上?
2. 路由系统深度解析
2.1 动态路径参数实战
FastAPI 的路由参数解析比 Flask 更加严谨。假设我们要构建一个博客系统,这种参数处理方式会直接影响 API 设计:
from fastapi import FastAPI app = FastAPI() @app.get("/posts/{post_id}") async def read_post(post_id: int): return {"post_id": post_id}这里有个容易踩坑的地方:如果客户端传入了非整数字符串,FastAPI 会自动返回 422 错误。但在生产环境中,我们可能需要自定义错误信息:
from fastapi import HTTPException @app.get("/posts/{post_id}") async def read_post(post_id: int): if post_id < 1: raise HTTPException( status_code=400, detail="Post ID must be positive integer" ) return {"post_id": post_id}2.2 查询参数的高级用法
分页查询是实际项目中最常见的场景之一。FastAPI 对可选参数的处理非常优雅:
from typing import Optional @app.get("/posts/") async def list_posts( page: int = 1, per_page: int = 10, search: Optional[str] = None ): skip = (page - 1) * per_page # 实际项目这里会连接数据库 return { "page": page, "per_page": per_page, "search_term": search, "data": [] }注意参数默认值的设置技巧:
- 分页参数建议设置合理的默认值
- 搜索参数使用 Optional 明确标识可选性
- 布尔型参数应该用
query_param: bool = False形式
3. 请求体与数据验证
3.1 Pydantic 模型实战
FastAPI 的数据验证核心在于 Pydantic。假设我们要处理用户注册:
from pydantic import BaseModel, EmailStr from datetime import date class UserCreate(BaseModel): username: str email: EmailStr password: str birth_date: date interests: list[str] = [] @app.post("/users/") async def create_user(user: UserCreate): # 密码应该哈希处理 user_dict = user.dict() user_dict.pop("password") return {"user": user_dict}几个关键验证点:
- EmailStr 会自动验证邮箱格式
- birth_date 会验证日期格式
- interests 默认为空列表
3.2 表单数据处理
当处理 HTML 表单时,需要额外安装依赖:
pip install python-multipart然后可以这样处理表单提交:
from fastapi import Form @app.post("/login/") async def login( username: str = Form(...), password: str = Form(...) ): return {"username": username}注意 Form 和 Body 的区别:
- Form 用于传统网页表单
- Body 用于 JSON API
- 不能混用这两种方式
4. 模板渲染实战
4.1 Jinja2 集成
虽然 FastAPI 以 API 见长,但渲染网页也很方便。首先安装依赖:
pip install jinja2配置模板系统:
from fastapi.templating import Jinja2Templates templates = Jinja2Templates(directory="templates") @app.get("/", response_class=HTMLResponse) async def home(request: Request): return templates.TemplateResponse( "index.html", {"request": request, "title": "首页"} )模板文件templates/index.html:
<!DOCTYPE html> <html> <head> <title>{{ title }}</title> </head> <body> <h1>Welcome to {{ title }}</h1> </body> </html>4.2 静态文件处理
静态文件配置很容易被忽略:
from fastapi.staticfiles import StaticFiles app.mount("/static", StaticFiles(directory="static"), name="static")最佳实践建议:
- CSS/JS 放在 static 目录
- 图片等资源建议使用 CDN
- 开发环境可以这样处理,生产环境建议用 Nginx
5. 常见问题排查
5.1 路由冲突问题
当定义下面两个路由时:
@app.get("/users/me") async def current_user(): return {"user": "current"} @app.get("/users/{user_id}") async def get_user(user_id: str): return {"user_id": user_id}必须注意顺序!如果把/users/{user_id}放在前面,/users/me将永远无法匹配。
5.2 异步上下文陷阱
在异步函数中使用数据库连接时:
# 错误示范! @app.get("/posts/") async def list_posts(): conn = get_db_conn() # 同步连接 posts = conn.execute("SELECT...") # 同步操作 return posts应该使用异步数据库驱动,如 asyncpg 或 SQLAlchemy 1.4+:
@app.get("/posts/") async def list_posts(): async with async_db_session() as session: result = await session.execute(select(Post)) return result.scalars().all()5.3 部署注意事项
虽然问题提到 IIS,但 Windows 部署更推荐:
- 使用 WSL 运行 Linux 环境
- 或者用 waitress 作为 WSGI 服务器:
from waitress import serve serve(app, host="0.0.0.0", port=8000)生产环境最佳实践:
- 使用 Gunicorn + Uvicorn 组合
- 配置 Nginx 反向代理
- 启用 HTTPS
6. 性能优化技巧
6.1 依赖项缓存
对于昂贵的初始化操作,使用 lru_cache:
from functools import lru_cache @lru_cache def get_ml_model(): print("Loading big ML model...") return pretend_big_model() @app.get("/predict") async def predict(input: str): model = get_ml_model() return model.predict(input)6.2 响应模型优化
使用 response_model 过滤返回字段:
class UserPublic(BaseModel): username: str email: EmailStr @app.post("/users/", response_model=UserPublic) async def create_user(user: UserCreate): # 返回包含密码的完整用户数据 return user这样即使处理函数返回了密码字段,响应中也会自动过滤掉。
7. 项目结构建议
第二天结束时,建议采用这样的结构:
my_project/ ├── app/ │ ├── __init__.py │ ├── main.py │ ├── routers/ │ │ ├── posts.py │ │ └── users.py │ ├── models/ │ ├── schemas/ │ └── static/ ├── tests/ └── requirements.txt关键点:
- 按功能拆分路由文件
- 分离数据模型和 Pydantic 模型
- 静态文件单独目录
- 早期就要考虑测试目录
8. 第二天学习路线建议
上午:
- 巩固路由和请求处理
- 练习 Pydantic 模型定义
下午:
- 实现一个简单的 CRUD 接口
- 集成 Jinja2 模板
晚上:
- 尝试部署到本地服务器
- 编写简单的测试用例
我自己的经验是,第二天结束时应该能:
- 独立设计 RESTful 接口
- 处理表单提交和文件上传
- 渲染基本模板页面
- 理解基本的异步编程概念