FastAPI 实战全攻略:从零基础到高性能 API 部署,教你吃透核心玩法

部署运行你感兴趣的模型镜像

目录

引言

一、为什么选 FastAPI?—— 对比传统框架的核心优势

二、基础入门:环境搭建与第一个 FastAPI 应用

2.1 环境搭建

2.1.1 安装依赖

2.1.2 验证安装

2.2 第一个 FastAPI 应用:Hello World

启动应用

访问接口

自动生成的接口文档

三、核心功能:API 接口开发的核心玩法

3.1 路径参数:动态匹配 URL 路径

基础示例:获取用户信息

测试接口

路径参数约束:枚举类型

3.2 查询参数:URL 中的键值对参数

基础示例:分页查询用户列表

测试接口

可选查询参数

3.3 请求体:接收 POST/PUT 请求的数据

步骤 1:定义 Pydantic 模型

步骤 2:编写 POST 接口

测试接口

3.4 响应模型:规范接口返回格式

示例:定义用户响应模型

四、进阶功能:解锁 FastAPI 的高级玩法

4.1 依赖注入:解耦代码,提升复用性

示例 1:简单依赖项(复用公共逻辑)

示例 2:带参数的依赖项(数据库连接)

4.2 认证授权:保护接口安全

步骤 1:安装依赖

步骤 2:实现 JWT 认证逻辑

测试认证流程

4.3 异步处理:提升高并发性能

示例:异步接口与异步数据库操作

4.4 数据验证:Pydantic 模型的高级用法

示例:带验证规则的用户模型

验证效果

五、实战项目:构建完整的 TODO List API

5.1 项目结构

5.2 依赖文件(requirements.txt)

5.3 主程序(main.py)

5.4 运行与测试

测试流程

六、部署上线:从开发环境到生产环境

6.1 生产环境启动(Uvicorn + Gunicorn)

安装依赖

启动命令

6.2 Docker 容器化部署

步骤 1:创建 Dockerfile

步骤 2:创建.dockerignore 文件

步骤 3:构建并运行容器

6.3 部署注意事项

七、避坑指南:常见问题与解决方案

7.1 坑点 1:路径参数与查询参数混淆

7.2 坑点 2:Pydantic 模型未启用 ORM 模式

7.3 坑点 3:异步接口中使用同步 IO 操作

7.4 坑点 4:生产环境启用 reload 模式

八、总结:FastAPI 学习路径与进阶建议

8.1 核心要点回顾

8.2 进阶学习建议


 

class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

引言

在 Python Web 框架生态中,FastAPI 凭借 “高性能、易上手、自动生成文档” 三大核心优势异军突起,短短几年就成为开发者构建 API 的首选工具。它不仅能轻松应对高并发场景,还能通过类型提示实现自动数据验证和接口文档生成,彻底解决了传统框架 “开发慢、调试繁、文档乱” 的痛点。

本文从 FastAPI 基础概念切入,循序渐进讲解核心功能,搭配完整实战案例,从环境搭建、接口开发到部署上线,全程手把手教学,帮你快速掌握 FastAPI 的开发技巧,轻松构建高性能 API 服务。

一、为什么选 FastAPI?—— 对比传统框架的核心优势

在 FastAPI 出现之前,Python 开发者构建 API 主要依赖 Flask 和 Django:

  • Flask:轻量灵活,但需要手动处理数据验证、文档编写,扩展性差,高并发场景下性能不足;
  • Django:功能全面,但笨重冗余,自带的 Admin 后台、ORM 等组件并非 API 开发必需,且同步架构难以应对高并发。

而 FastAPI 完美弥补了这些短板,核心优势如下:

  1. 性能强悍:基于 Starlette 和 Pydantic 开发,支持异步处理,性能接近 Node.js 和 Go,远超 Flask;
  2. 开发高效:通过 Python 类型提示自动完成数据验证、接口文档生成,无需手动编写文档;
  3. 易用性强:语法简洁直观,新手可快速上手,同时支持复杂场景(依赖注入、认证授权、WebSocket 等);
  4. 生态完善:兼容 FastAPI、Starlette 的组件,支持多种数据库和部署方式,扩展性强。

一组简单的性能对比(处理单请求响应时间):

  • Flask:约 10ms
  • Django:约 15ms
  • FastAPI:约 2ms

无论是开发个人项目、企业内部接口,还是高并发的生产级 API,FastAPI 都能轻松胜任。

二、基础入门:环境搭建与第一个 FastAPI 应用

2.1 环境搭建

2.1.1 安装依赖

FastAPI 的运行需要依赖uvicorn(ASGI 服务器,用于启动应用),推荐使用虚拟环境隔离依赖:

# 创建虚拟环境(Python 3.7+)
python -m venv fastapi-env

# 激活虚拟环境(Windows)
fastapi-env\Scripts\activate
# 激活虚拟环境(Linux/Mac)
source fastapi-env/bin/activate

# 安装FastAPI和uvicorn
pip install fastapi uvicorn

2.1.2 验证安装

安装完成后,运行以下命令查看版本:

pip list | grep fastapi  # 输出fastapi及其版本,如fastapi 0.104.1
pip list | grep uvicorn  # 输出uvicorn及其版本,如uvicorn 0.24.0

2.2 第一个 FastAPI 应用:Hello World

新建文件main.py,编写最简单的 FastAPI 应用:

# 导入FastAPI类
from fastapi import FastAPI

# 创建FastAPI应用实例
app = FastAPI()

# 定义路由:GET请求,路径为"/"
@app.get("/")
def read_root():
    # 返回字典,FastAPI自动转为JSON响应
    return {"message": "Hello FastAPI!"}

启动应用

在终端运行以下命令启动服务:

uvicorn main:app --reload
  • main:Python 文件名(main.py);
  • app:FastAPI 实例名(app = FastAPI());
  • --reload:开发模式,代码修改后自动重启服务(生产环境禁用)。

启动成功后,终端会输出:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [xxxx] using WatchFiles
INFO:     Started server process [xxxx]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

访问接口

打开浏览器访问 http://127.0.0.1:8000,会看到 JSON 响应:

{"message":"Hello FastAPI!"}

自动生成的接口文档

FastAPI 最强大的特性之一是自动生成接口文档,无需手动编写:

  • Swagger UI 文档:http://127.0.0.1:8000/docs(支持在线调试);
  • ReDoc 文档:http://127.0.0.1:8000/redoc(更简洁的文档展示)。

打开 Swagger UI 文档,点击Try it out即可测试接口,非常方便。

三、核心功能:API 接口开发的核心玩法

3.1 路径参数:动态匹配 URL 路径

路径参数用于从 URL 中提取动态数据,比如用户 ID、商品编号等,通过{参数名}定义。

基础示例:获取用户信息

from fastapi import FastAPI

app = FastAPI()

# 路径参数user_id,类型提示为int
@app.get("/users/{user_id}")
def get_user(user_id: int):
    return {"user_id": user_id, "message": "用户信息查询成功"}

测试接口

  • 访问 http://127.0.0.1:8000/users/100,返回:{"user_id":100,"message":"用户信息查询成功"}
  • 若访问 http://127.0.0.1:8000/users/abc,会自动返回 422 错误:{"detail":[{"loc":["path","user_id"],"msg":"value is not a valid integer","type":"type_error.integer"}]},这就是 FastAPI 的自动数据验证功能。

路径参数约束:枚举类型

如果路径参数只能是固定值(比如性别、状态),可以使用枚举类型限制:

from fastapi import FastAPI
from enum import Enum

app = FastAPI()

# 定义枚举类
class Gender(str, Enum):
    MALE = "male"
    FEMALE = "female"
    OTHER = "other"

# 路径参数gender必须是Gender枚举中的值
@app.get("/users/gender/{gender}")
def get_users_by_gender(gender: Gender):
    return {"gender": gender.value, "count": 100}

访问 http://127.0.0.1:8000/users/gender/male,返回:{"gender":"male","count":100};若传入其他值,会自动提示合法选项。

3.2 查询参数:URL 中的键值对参数

查询参数是 URL 中?后的键值对,用于过滤、分页等场景,FastAPI 会自动识别函数中未定义为路径参数的参数作为查询参数。

基础示例:分页查询用户列表

from fastapi import FastAPI

app = FastAPI()

# 模拟用户数据
users = [
    {"id": 1, "name": "张三", "age": 20},
    {"id": 2, "name": "李四", "age": 25},
    {"id": 3, "name": "王五", "age": 30},
    {"id": 4, "name": "赵六", "age": 35},
]

# page:页码,默认1;limit:每页数量,默认2
@app.get("/users")
def get_users(page: int = 1, limit: int = 2):
    # 计算切片起始和结束索引
    start = (page - 1) * limit
    end = start + limit
    return {
        "page": page,
        "limit": limit,
        "total": len(users),
        "data": users[start:end]
    }

测试接口

  • 访问 http://127.0.0.1:8000/users,默认返回第 1 页,每页 2 条数据;
  • 访问 http://127.0.0.1:8000/users?page=2&limit=1,返回第 2 页,每页 1 条数据:

    json

    {"page":2,"limit":1,"total":4,"data":[{"id":3,"name":"王五","age":30}]}
    

可选查询参数

如果查询参数可选,可以设置默认值为None

@app.get("/users/search")
def search_users(name: str | None = None, age: int | None = None):
    # 过滤用户
    result = users
    if name:
        result = [user for user in result if name in user["name"]]
    if age:
        result = [user for user in result if user["age"] == age]
    return {"data": result}

访问 http://127.0.0.1:8000/users/search?name=李,返回:{"data":[{"id":2,"name":"李四","age":25}]}

3.3 请求体:接收 POST/PUT 请求的数据

请求体用于接收客户端发送的大量数据(如创建用户、更新信息),FastAPI 通过Pydantic 模型定义请求体结构,自动完成数据验证和类型转换。

步骤 1:定义 Pydantic 模型

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# 定义请求体模型(数据验证)
class UserCreate(BaseModel):
    name: str  # 必选字段,字符串类型
    age: int  # 必选字段,整数类型
    email: str | None = None  # 可选字段,字符串类型,默认None
    is_active: bool = True  # 可选字段,布尔类型,默认True

步骤 2:编写 POST 接口

# 模拟数据库
db_users = []

@app.post("/users", status_code=201)  # status_code设置响应状态码
def create_user(user: UserCreate):
    # 将Pydantic模型转为字典,添加到数据库
    user_dict = user.dict()
    user_dict["id"] = len(db_users) + 1  # 生成自增ID
    db_users.append(user_dict)
    return {"message": "用户创建成功", "data": user_dict}

测试接口

打开 Swagger 文档(/docs),找到/users的 POST 接口,点击Try it out,输入请求体:

{
  "name": "孙七",
  "age": 28,
  "email": "sunqi@example.com"
}

点击Execute,返回:

{
  "message": "用户创建成功",
  "data": {
    "name": "孙七",
    "age": 28,
    "email": "sunqi@example.com",
    "is_active": true,
    "id": 1
  }
}

若请求体不符合模型定义(如age传入字符串),会自动返回验证错误。

3.4 响应模型:规范接口返回格式

响应模型用于定义接口的返回结构,确保返回数据的一致性,同时自动过滤不需要的字段。

示例:定义用户响应模型

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# 定义请求体模型
class UserCreate(BaseModel):
    name: str
    age: int
    email: str | None = None
    is_active: bool = True

# 定义响应模型(过滤is_active字段)
class UserResponse(BaseModel):
    id: int
    name: str
    age: int
    email: str | None = None

    # 配置:允许接收额外字段(如数据库返回的其他字段)
    class Config:
        orm_mode = True  # 支持ORM对象直接转换

# 模拟数据库
db_users = []

@app.post("/users", response_model=UserResponse, status_code=201)
def create_user(user: UserCreate):
    user_dict = user.dict()
    user_dict["id"] = len(db_users) + 1
    db_users.append(user_dict)
    return user_dict  # 自动按照UserResponse格式返回,过滤is_active

测试接口后,返回结果中不会包含is_active字段,确保响应格式统一。

四、进阶功能:解锁 FastAPI 的高级玩法

4.1 依赖注入:解耦代码,提升复用性

依赖注入是 FastAPI 的核心特性之一,用于管理可复用的资源(如数据库连接、认证逻辑、日志工具),避免代码重复,提升可维护性。

示例 1:简单依赖项(复用公共逻辑)

from fastapi import FastAPI, Depends

app = FastAPI()

# 定义依赖项:获取当前时间
def get_current_time():
    from datetime import datetime
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

# 接口依赖get_current_time,自动注入返回值
@app.get("/hello")
def hello(current_time: str = Depends(get_current_time)):
    return {"message": "Hello FastAPI!", "current_time": current_time}

访问接口,返回:{"message":"Hello FastAPI!","current_time":"2024-10-01 15:30:00"}

示例 2:带参数的依赖项(数据库连接)

from fastapi import FastAPI, Depends
import sqlite3

app = FastAPI()

# 定义带参数的依赖项:数据库连接
def get_db(db_name: str = "test.db"):
    # 建立数据库连接
    conn = sqlite3.connect(db_name)
    conn.row_factory = sqlite3.Row  # 使查询结果可通过列名访问
    try:
        yield conn  # 提供连接给接口使用
    finally:
        conn.close()  # 接口执行完成后关闭连接

# 接口依赖数据库连接
@app.get("/db/users")
def get_db_users(db = Depends(get_db)):
    cursor = db.cursor()
    cursor.execute("SELECT * FROM users")  # 假设users表已存在
    users = [dict(row) for row in cursor.fetchall()]
    return {"data": users}

依赖注入确保了数据库连接的统一管理,避免资源泄露。

4.2 认证授权:保护接口安全

FastAPI 支持多种认证方式,最常用的是 OAuth2 + JWT(JSON Web Token),用于保护需要登录才能访问的接口。

步骤 1:安装依赖

pip install python-jose[cryptography] passlib[bcrypt]

步骤 2:实现 JWT 认证逻辑

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta

app = FastAPI()

# 配置项(实际项目中应放在环境变量中)
SECRET_KEY = "your-secret-key-keep-it-safe"  # 密钥,用于签名JWT
ALGORITHM = "HS256"  # 加密算法
ACCESS_TOKEN_EXPIRE_MINUTES = 30  # Token过期时间(30分钟)

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# OAuth2密码模式
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 模拟用户数据库(实际项目中应连接真实数据库)
fake_users_db = {
    "admin": {
        "username": "admin",
        "full_name": "Admin User",
        "email": "admin@example.com",
        "hashed_password": pwd_context.hash("admin123"),  # 加密后的密码
        "disabled": False,
    }
}

# 验证密码
def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

# 获取用户
def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return user_dict
    return None

# 验证用户并生成Token
def authenticate_user(fake_db, username: str, password: str):
    user = get_user(fake_db, username)
    if not user:
        return False
    if not verify_password(password, user["hashed_password"]):
        return False
    return user

# 创建访问Token
def create_access_token(data: dict):
    to_encode = data.copy()
    # 设置过期时间
    expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    # 生成Token
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# 获取当前用户(依赖项)
async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无效的认证凭据",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        # 解码Token
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    user = get_user(fake_users_db, username=username)
    if user is None:
        raise credentials_exception
    return user

# 获取Token接口(登录接口)
@app.post("/token")
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(fake_users_db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户名或密码错误",
            headers={"WWW-Authenticate": "Bearer"},
        )
    # 生成Token,sub字段存储用户名
    access_token = create_access_token(data={"sub": user["username"]})
    return {"access_token": access_token, "token_type": "bearer"}

# 受保护的接口(需要登录才能访问)
@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
    return current_user

测试认证流程

  1. 访问 /docs,先调用/token接口,输入用户名admin和密码admin123,获取 Token;
  2. 调用/users/me接口,点击Authorize,输入Bearer + Token,即可访问接口,返回当前用户信息。

4.3 异步处理:提升高并发性能

FastAPI 原生支持异步编程,通过async/await语法编写异步接口,能高效处理 IO 密集型任务(如数据库查询、网络请求),提升系统并发能力。

示例:异步接口与异步数据库操作

from fastapi import FastAPI
import asyncio
# 假设使用异步数据库驱动(如asyncpg for PostgreSQL)
from async_fake_db import AsyncFakeDB  # 模拟异步数据库

app = FastAPI()
db = AsyncFakeDB()

# 异步接口:创建用户
@app.post("/async/users")
async def create_async_user(name: str, age: int):
    # 异步数据库操作(await等待操作完成)
    user_id = await db.insert_user({"name": name, "age": age})
    # 模拟异步IO延迟(如网络请求)
    await asyncio.sleep(0.1)
    return {"message": "异步创建用户成功", "user_id": user_id}

# 异步接口:查询用户
@app.get("/async/users/{user_id}")
async def get_async_user(user_id: int):
    user = await db.get_user(user_id)
    if not user:
        return {"message": "用户不存在"}
    return {"data": user}

异步接口的优势在于:当处理 IO 任务时,系统可以同时处理其他请求,而无需等待 IO 完成,大幅提升高并发场景下的吞吐量。

4.4 数据验证:Pydantic 模型的高级用法

Pydantic 模型不仅能定义数据结构,还支持复杂的数据验证规则,确保输入数据的合法性。

示例:带验证规则的用户模型

from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
from typing import List

app = FastAPI()

# 定义带验证规则的模型
class User(BaseModel):
    # 姓名:必填,长度2-20
    name: str = Field(..., min_length=2, max_length=20, description="用户姓名")
    # 年龄:必填,18-60岁
    age: int = Field(..., ge=18, le=60, description="用户年龄(18-60岁)")
    # 邮箱:必填,格式验证
    email: EmailStr = Field(..., description="用户邮箱")
    # 爱好:可选,列表类型
    hobbies: List[str] = Field(default=[], description="用户爱好")

@app.post("/users/validate")
def create_validated_user(user: User):
    return {"message": "用户创建成功", "data": user.dict()}

验证效果

  • name长度为 1,返回错误:{"detail":[{"loc":["body","name"],"msg":"ensure this value has at least 2 characters","type":"value_error.any_str.min_length","ctx":{"limit_value":2}}]}
  • email格式错误(如test.com),返回错误:{"detail":[{"loc":["body","email"],"msg":"value is not a valid email address","type":"value_error.email"}]}

五、实战项目:构建完整的 TODO List API

结合前面的知识点,我们构建一个完整的 TODO List API,实现任务的 CRUD(创建、查询、更新、删除)操作,集成数据库、依赖注入、数据验证等功能。

5.1 项目结构

todo_api/
├── main.py  # 主程序
└── requirements.txt  # 依赖文件

5.2 依赖文件(requirements.txt)

fastapi>=0.100.0
uvicorn>=0.20.0
sqlalchemy>=2.0.0  # ORM数据库
pydantic>=2.0.0
python-multipart>=0.0.6

5.3 主程序(main.py)

from fastapi import FastAPI, Depends, HTTPException, status
from pydantic import BaseModel, Field
from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from typing import List, Optional

# ---------------------- 数据库配置 ----------------------
# 数据库连接地址(SQLite,文件型数据库,无需额外安装)
SQLALCHEMY_DATABASE_URL = "sqlite:///./todo.db"

# 创建数据库引擎
engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}  # SQLite必需参数
)

# 创建会话本地类
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 声明基类
Base = declarative_base()

# ---------------------- 数据库模型 ----------------------
# TODO任务模型(对应数据库表)
class TodoDB(Base):
    __tablename__ = "todos"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True, nullable=False)  # 任务标题
    description = Column(String, index=True, nullable=True)  # 任务描述
    completed = Column(Boolean, default=False)  # 完成状态

# 创建数据库表(首次运行时执行)
Base.metadata.create_all(bind=engine)

# ---------------------- 依赖项 ----------------------
# 获取数据库会话
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# ---------------------- Pydantic模型 ----------------------
# 请求模型:创建任务
class TodoCreate(BaseModel):
    title: str = Field(..., min_length=1, max_length=100, description="任务标题")
    description: Optional[str] = Field(None, max_length=500, description="任务描述")

# 响应模型:任务详情
class TodoResponse(BaseModel):
    id: int
    title: str
    description: Optional[str]
    completed: bool

    class Config:
        orm_mode = True  # 支持ORM对象转换

# 请求模型:更新任务
class TodoUpdate(BaseModel):
    title: Optional[str] = Field(None, min_length=1, max_length=100)
    description: Optional[str] = Field(None, max_length=500)
    completed: Optional[bool] = None

# ---------------------- FastAPI应用 ----------------------
app = FastAPI(title="TODO List API", description="基于FastAPI的任务管理API", version="1.0.0")

# ---------------------- 接口实现 ----------------------
# 1. 创建任务(POST)
@app.post("/todos", response_model=TodoResponse, status_code=status.HTTP_201)
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
    # 创建数据库对象
    db_todo = TodoDB(
        title=todo.title,
        description=todo.description
    )
    # 添加到数据库
    db.add(db_todo)
    db.commit()
    db.refresh(db_todo)  # 刷新对象,获取自增ID
    return db_todo

# 2. 查询所有任务(GET)
@app.get("/todos", response_model=List[TodoResponse])
def get_all_todos(
    skip: int = 0,  # 跳过前N条
    limit: int = 10,  # 最多返回N条
    db: Session = Depends(get_db)
):
    todos = db.query(TodoDB).offset(skip).limit(limit).all()
    return todos

# 3. 查询单个任务(GET)
@app.get("/todos/{todo_id}", response_model=TodoResponse)
def get_todo(todo_id: int, db: Session = Depends(get_db)):
    # 查询任务
    todo = db.query(TodoDB).filter(TodoDB.id == todo_id).first()
    # 任务不存在则返回404
    if not todo:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"任务ID {todo_id} 不存在"
        )
    return todo

# 4. 更新任务(PUT)
@app.put("/todos/{todo_id}", response_model=TodoResponse)
def update_todo(
    todo_id: int,
    todo_update: TodoUpdate,
    db: Session = Depends(get_db)
):
    # 查询任务
    todo = db.query(TodoDB).filter(TodoDB.id == todo_id).first()
    if not todo:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"任务ID {todo_id} 不存在"
        )
    # 更新字段(只更新传入的非空字段)
    if todo_update.title is not None:
        todo.title = todo_update.title
    if todo_update.description is not None:
        todo.description = todo_update.description
    if todo_update.completed is not None:
        todo.completed = todo_update.completed
    # 提交更新
    db.commit()
    db.refresh(todo)
    return todo

# 5. 删除任务(DELETE)
@app.delete("/todos/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
    # 查询任务
    todo = db.query(TodoDB).filter(TodoDB.id == todo_id).first()
    if not todo:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"任务ID {todo_id} 不存在"
        )
    # 删除任务
    db.delete(todo)
    db.commit()
    return None  # 204状态码无返回内容

5.4 运行与测试

  1. 安装依赖:pip install -r requirements.txt
  2. 启动服务:uvicorn main:app --reload
  3. 访问 Swagger 文档:http://127.0.0.1:8000/docs,测试所有接口。

测试流程

  1. 调用/todos的 POST 接口,创建一个任务;
  2. 调用/todos的 GET 接口,查看所有任务;
  3. 调用/todos/{todo_id}的 GET 接口,查看单个任务;
  4. 调用/todos/{todo_id}的 PUT 接口,更新任务状态为完成;
  5. 调用/todos/{todo_id}的 DELETE 接口,删除任务。

六、部署上线:从开发环境到生产环境

6.1 生产环境启动(Uvicorn + Gunicorn)

开发环境使用uvicorn --reload,生产环境需要更稳定的部署方式,推荐使用Gunicorn作为进程管理器,搭配Uvicorn作为工作进程。

安装依赖

pip install gunicorn

启动命令

gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app --bind 0.0.0.0:8000
  • -w 4:启动 4 个工作进程(建议为 CPU 核心数的 2-4 倍);
  • -k:指定工作进程类型为 UvicornWorker;
  • --bind:绑定地址和端口,0.0.0.0 表示允许外部访问。

6.2 Docker 容器化部署

容器化部署能确保环境一致性,简化部署流程,以下是 Docker 部署步骤。

步骤 1:创建 Dockerfile

# 基础镜像
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件
COPY requirements.txt .

# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制项目代码
COPY . .

# 暴露端口
EXPOSE 8000

# 启动命令
CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "main:app", "--bind", "0.0.0.0:8000"]

步骤 2:创建.dockerignore 文件

__pycache__/
*.pyc
*.pyo
*.pyd
.env
*.db

步骤 3:构建并运行容器

# 构建镜像
docker build -t todo-api .

# 运行容器
docker run -d -p 8000:8000 --name todo-api-container todo-api

访问 http://localhost:8000/docs,即可使用部署后的 API。

6.3 部署注意事项

  1. 密钥管理:生产环境中,SECRET_KEY、数据库连接地址等敏感信息应放在环境变量中,避免硬编码;
  2. 日志配置:配置日志输出,便于问题排查;
  3. 数据库迁移:使用 Alembic 管理数据库迁移,避免直接删除或修改表结构;
  4. HTTPS 配置:生产环境应启用 HTTPS,可通过 Nginx 反向代理实现。

七、避坑指南:常见问题与解决方案

7.1 坑点 1:路径参数与查询参数混淆

问题:误将路径参数写成查询参数,导致接口无法访问。示例

# 错误:将user_id作为查询参数,却用了路径参数的URL
@app.get("/users")
def get_user(user_id: int):
    return {"user_id": user_id}

解决方案:明确区分路径参数和查询参数,路径参数需在 URL 路径中定义:

# 正确:user_id为路径参数
@app.get("/users/{user_id}")
def get_user(user_id: int):
    return {"user_id": user_id}

7.2 坑点 2:Pydantic 模型未启用 ORM 模式

问题:返回 ORM 对象时,提示 “value is not a valid dict”。解决方案:在响应模型中启用orm_mode = True

class TodoResponse(BaseModel):
    id: int
    title: str

    class Config:
        orm_mode = True

7.3 坑点 3:异步接口中使用同步 IO 操作

问题:异步接口中使用同步数据库驱动(如 sqlite3),导致性能下降。解决方案:使用异步数据库驱动(如 asyncpg、motor),或在同步操作前加asyncio.to_thread

# 同步操作转为异步
result = await asyncio.to_thread(sync_db_operation)

7.4 坑点 4:生产环境启用 reload 模式

问题:生产环境使用--reload参数,导致性能下降和安全风险。解决方案:生产环境启动时移除--reload参数。

八、总结:FastAPI 学习路径与进阶建议

 

8.1 核心要点回顾

  1. 基础功能:掌握路径参数、查询参数、请求体、响应模型的使用,这是 API 开发的基础;
  2. 进阶功能:依赖注入用于解耦代码,认证授权保护接口安全,异步处理提升并发性能;
  3. 实战能力:通过完整项目掌握数据库集成、接口设计、部署上线的全流程;
  4. 避坑技巧:注意参数类型、模型配置、异步操作、生产环境部署的细节。

8.2 进阶学习建议

  1. 深入 Pydantic:学习 Pydantic 的高级验证规则、模型嵌套、自定义验证器;
  2. WebSocket 支持:FastAPI 原生支持 WebSocket,可用于实时通信场景(如聊天、通知);
  3. 测试与文档:使用 pytest 测试 API,自定义接口文档内容;
  4. 性能优化:学习数据库索引优化、缓存使用、负载均衡等技巧。

FastAPI 的核心优势在于 “简单易用且高性能”,只要掌握核心知识点,多动手实战,就能快速构建出高质量的 API 服务。无论是个人项目还是企业级应用,FastAPI 都是 Python 开发者的优质选择。

 

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值