Python-FastAPI-完全教程笔记-全-
Python FastAPI 完全教程笔记(全)
001:第 1 部分



概述

在本课程中,我们将学习如何使用 FastAPI 框架构建 Web 应用程序。FastAPI 是一个流行的 Python Web 框架,因其高性能、简洁性以及对现代 Python 特性的支持而广受欢迎。在本系列视频中,我们将探讨该框架的不同方面,并学习如何利用它们在一个简单的 Web 应用程序中实现功能。







环境设置






上一节我们介绍了课程概述,本节中我们来看看如何设置开发环境。
要开始我们的项目,首先需要创建一个文件夹来安装项目依赖。
以下是创建和激活虚拟环境的步骤:
- 打开 VS Code 并创建一个新文件夹,例如
bookley。 - 在 VS Code 中打开该文件夹。
- 打开终端,使用 Python 的
venv模块创建虚拟环境:python3 -m venv venv - 激活虚拟环境:
- Linux/MacOS:
source venv/bin/activate - Windows:
venv\Scripts\activate
- Linux/MacOS:
- 激活后,确认 Python 解释器指向虚拟环境:
which python3




安装 FastAPI





现在我们已经激活了虚拟环境,接下来安装 FastAPI。


使用 pip 安装 FastAPI:
pip install fastapi


安装完成后,可以通过导入模块来验证安装:
import fastapi


或者,通过运行 FastAPI CLI 来验证:
fastapi
创建简单的 Web 服务器


上一节我们完成了环境设置和 FastAPI 安装,本节中我们来看看如何创建一个简单的 Web 服务器。


首先,创建一个名为 main.py 的文件。


在 main.py 中,导入 FastAPI 类并创建一个应用实例:
from fastapi import FastAPI


app = FastAPI()
接下来,定义一个处理根路径 GET 请求的路由:
@app.get("/")
async def read_root():
return {"message": "Hello World"}
要运行这个服务器,可以使用 FastAPI 的开发服务器命令:
fastapi dev main.py
服务器将在 http://localhost:8000 启动。访问该地址,你将看到返回的 JSON 消息。

定义更多端点



上一节我们创建了一个简单的 Web 服务器,本节中我们来看看如何定义更多端点以返回不同的响应。
我们将创建一个用于问候用户的端点。
以下是创建带路径参数的问候端点的步骤:
- 定义一个 GET 端点,路径为
/greet/{name}。 - 在路径处理函数中接收
name参数。 - 返回包含问候消息的字典。
代码示例如下:
@app.get("/greet/{name}")
async def greet_name(name: str):
return {"message": f"Hello {name}"}
访问 http://localhost:8000/greet/Jonathan,服务器将返回 {"message": "Hello Jonathan"}。
使用 HTTP 客户端
到目前为止,我们一直使用浏览器作为 HTTP 客户端。但在实际开发中,使用专门的 HTTP 客户端工具(如 Postman、Insomnia 或 RESTfox)会更方便。
我们将使用 RESTfox 来测试我们的 API 端点。




以下是使用 RESTfox 的基本步骤:
- 打开 RESTfox 并创建一个新的工作空间。
- 在工作空间中创建一个新的 HTTP 请求。
- 设置请求方法(如 GET)和目标 URL(如
http://localhost:8000/)。 - 发送请求并查看响应。
查询参数
除了路径参数,FastAPI 还允许我们通过查询参数向服务器发送数据。
查询参数是通过 URL 中的键值对传递的,例如 ?name=Jonathan。
以下是如何在 FastAPI 中定义和使用查询参数:
- 在路径处理函数中定义一个参数(例如
name),但不将其包含在 URL 路径中。 - FastAPI 会自动将其视为查询参数。
- 可以指定参数类型(如
str)和默认值。
代码示例如下:
@app.get("/greet")
async def greet_user(name: str = "User"):
return {"message": f"Hello {name}"}
访问 http://localhost:8000/greet?name=Jonathan,服务器将返回 {"message": "Hello Jonathan"}。
混合路径参数和查询参数
在某些情况下,我们可能需要在同一个端点中同时使用路径参数和查询参数。
以下是如何混合使用路径参数和查询参数:
- 在 URL 路径中定义路径参数(如
{name})。 - 在路径处理函数中定义额外的参数作为查询参数(如
age)。 - 发送请求时,在 URL 中提供路径参数,在查询字符串中提供查询参数。
代码示例如下:
@app.get("/greet/{name}")
async def greet_user_with_age(name: str, age: int = 0):
return {"message": f"Hello {name}, you are {age} years old"}
访问 http://localhost:8000/greet/Jonathan?age=23,服务器将返回 {"message": "Hello Jonathan, you are 23 years old"}。
可选查询参数
有时,我们希望查询参数是可选的,并在未提供时使用默认值。
以下是如何定义可选查询参数:



- 从
typing模块导入Optional类。 - 将参数类型定义为
Optional[str]或Optional[int]。 - 为参数提供默认值。



代码示例如下:
from typing import Optional


@app.get("/greet")
async def greet_optional(name: Optional[str] = "User", age: Optional[int] = 0):
return {"message": f"Hello {name}, you are {age} years old"}
如果访问 http://localhost:8000/greet,服务器将返回 {"message": "Hello User, you are 0 years old"}。如果提供 name 和 age 参数,则使用提供的值。
请求体
除了路径参数和查询参数,我们还可以通过请求体向服务器发送数据,通常用于创建或更新资源。
以下是如何使用请求体发送数据:
- 定义一个 Pydantic 模型来验证请求体数据。
- 在路径处理函数中将该模型作为参数注入。
- 使用模型验证数据并返回响应。




代码示例如下:
from pydantic import BaseModel



class BookCreate(BaseModel):
title: str
author: str


@app.post("/createbook")
async def create_book(book: BookCreate):
return {"title": book.title, "author": book.author}

使用 RESTfox 发送 POST 请求到 http://localhost:8000/createbook,并在请求体中提供 JSON 数据(如 {"title": "Learn FastAPI", "author": "John Doe"}),服务器将返回创建的书本信息。
请求头
FastAPI 允许我们访问请求头信息,例如 User-Agent、Host 等。
以下是如何访问请求头:


- 从
fastapi导入Header函数。 - 在路径处理函数中定义参数,并使用
Header作为默认值。 - 返回包含请求头信息的字典。
代码示例如下:
from fastapi import Header
@app.get("/headers")
async def get_headers(user_agent: Optional[str] = Header(None), host: Optional[str] = Header(None)):
return {"User-Agent": user_agent, "Host": host}
访问 http://localhost:8000/headers,服务器将返回请求头信息。
状态码
我们可以自定义端点返回的 HTTP 状态码。
以下是如何指定状态码:


- 从
fastapi导入status模块。 - 在路由装饰器中指定
status_code参数。
代码示例如下:
from fastapi import status
@app.post("/createbook", status_code=status.HTTP_201_CREATED)
async def create_book(book: BookCreate):
return {"title": book.title, "author": book.author}
当创建资源成功时,服务器将返回状态码 201(Created)。
总结



本节课中我们一起学习了如何使用 FastAPI 构建简单的 Web 服务器。我们探讨了如何定义路由、使用路径参数和查询参数、处理请求体、访问请求头以及自定义状态码。通过这些基础知识,你已经能够创建功能丰富的 API 端点。在接下来的课程中,我们将深入探讨更高级的主题,如数据库集成、身份验证和授权等。
002:构建用户认证、授权、关系与部署
概述


在本节课中,我们将学习如何使用 FastAPI 构建一个完整的 API 端点,包括用户认证、授权、数据库关系、错误处理、中间件、电子邮件发送、后台任务、API 文档以及最终部署。我们将从获取当前登录用户开始,逐步实现角色管理、数据库关系、错误处理、中间件、电子邮件验证、密码重置、后台任务、API 文档和部署。


获取当前登录用户
上一节我们介绍了令牌验证,本节中我们来看看如何获取当前登录用户。
首先,我们需要创建一个依赖项来获取当前登录的用户。这个依赖项将使用我们之前创建的令牌验证依赖项。
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from source.db.main import get_session
from source.auth.service import UserService
from source.auth.models import User
async def get_current_user(
token: str = Depends(AccessTokenBearer()),
session: AsyncSession = Depends(get_session)
) -> User:
token_data = decode_access_token(token)
user_email = token_data.get("user", {}).get("email")
user_service = UserService()
user = await user_service.get_user_by_email(session, user_email)
return user
接下来,我们创建一个路由来获取当前登录用户。

from fastapi import APIRouter, Depends
from source.auth.dependencies import get_current_user

router = APIRouter()


@router.get("/me")
async def get_current_user_endpoint(user: User = Depends(get_current_user)):
return user
现在,我们可以测试这个端点。首先,我们需要获取一个有效的访问令牌,然后在请求头中添加授权头。
角色管理
上一节我们介绍了如何获取当前登录用户,本节中我们来看看如何实现角色管理。
首先,我们在用户模型中添加一个角色字段。
from sqlmodel import Field
from typing import Optional


class User(SQLModel, table=True):
role: Optional[str] = Field(default="user", sa_column_kwargs={"server_default": "user"})
接下来,我们创建一个依赖项来检查用户角色。
from fastapi import HTTPException, status
from typing import List
class RoleChecker:
def __init__(self, allowed_roles: List[str]):
self.allowed_roles = allowed_roles
async def __call__(self, current_user: User = Depends(get_current_user)):
if current_user.role in self.allowed_roles:
return True
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You are not permitted to perform this action."
)
现在,我们可以在路由中使用这个依赖项来限制访问。
from source.auth.dependencies import RoleChecker




admin_role_checker = RoleChecker(allowed_roles=["admin"])
@router.get("/admin-only", dependencies=[Depends(admin_role_checker)])
async def admin_only_endpoint():
return {"message": "This is an admin-only endpoint."}
数据库关系
上一节我们介绍了角色管理,本节中我们来看看如何建立数据库关系。
首先,我们在书籍模型中添加一个外键,关联到用户表。
from sqlmodel import Field, Relationship
from typing import Optional
class Book(SQLModel, table=True):
user_uid: Optional[str] = Field(default=None, foreign_key="user.uid")
user: Optional[User] = Relationship(back_populates="books")
接下来,我们在用户模型中添加反向关系。
class User(SQLModel, table=True):
books: List[Book] = Relationship(back_populates="user")
现在,我们可以在创建书籍时关联当前登录用户。
@router.post("/books")
async def create_book(
book_data: BookCreate,
user: User = Depends(get_current_user),
session: AsyncSession = Depends(get_session)
):
book_service = BookService()
book = await book_service.create_book(session, book_data, user.uid)
return book
错误处理
上一节我们介绍了数据库关系,本节中我们来看看如何自定义错误处理。
首先,我们创建一个自定义异常类。
class BooklyException(Exception):
"""Base class for all custom exceptions in the app."""
pass
class InvalidTokenError(BooklyException):
"""Raised when an invalid or expired token is provided."""
pass
接下来,我们创建一个异常处理器。
from fastapi import Request
from fastapi.responses import JSONResponse
def create_exception_handler(status_code: int, detail: Any):
async def exception_handler(request: Request, exc: Exception):
return JSONResponse(
content={"detail": detail},
status_code=status_code
)
return exception_handler
然后,我们将自定义异常注册到 FastAPI 应用中。



from source.errors import InvalidTokenError
app.add_exception_handler(InvalidTokenError, create_exception_handler(401, "Invalid token provided."))
中间件
上一节我们介绍了错误处理,本节中我们来看看如何使用中间件。



首先,我们创建一个日志中间件。





import time
from fastapi import Request
@app.middleware("http")
async def custom_logging_middleware(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
print(f"{request.method} {request.url.path} completed in {process_time:.2f}s")
return response
接下来,我们创建一个授权中间件。
from fastapi.responses import JSONResponse
@app.middleware("http")
async def authorization_middleware(request: Request, call_next):
if "authorization" not in request.headers:
return JSONResponse(
content={"message": "Not authenticated", "resolution": "Please provide valid credentials."},
status_code=401
)
response = await call_next(request)
return response






电子邮件发送
上一节我们介绍了中间件,本节中我们来看看如何发送电子邮件。


首先,我们配置电子邮件设置。








from fastapi_mail import FastMail, ConnectionConfig
config = ConnectionConfig(
MAIL_USERNAME="your-email@gmail.com",
MAIL_PASSWORD="your-app-password",
MAIL_FROM="your-email@gmail.com",
MAIL_PORT=587,
MAIL_SERVER="smtp.gmail.com",
MAIL_STARTTLS=True,
MAIL_SSL_TLS=False
)
fm = FastMail(config)
接下来,我们创建一个发送电子邮件的函数。
from fastapi_mail import MessageSchema
async def send_email(recipients: List[str], subject: str, body: str):
message = MessageSchema(
recipients=recipients,
subject=subject,
body=body,
subtype="html"
)
await fm.send_message(message)


现在,我们可以在用户注册时发送验证电子邮件。




@router.post("/signup")
async def signup(user_data: UserCreate, session: AsyncSession = Depends(get_session)):
user_service = UserService()
user = await user_service.create_user(session, user_data)
verification_token = create_url_safe_token({"email": user.email})
verification_link = f"{config.DOMAIN}/auth/verify/{verification_token}"
html_message = f"<h1>Verify your email</h1><p>Click <a href='{verification_link}'>here</a> to verify your email.</p>"
await send_email([user.email], "Verify your email", html_message)
return {"message": "Account created. Please check your email to verify your account."}
密码重置
上一节我们介绍了电子邮件发送,本节中我们来看看如何实现密码重置。
首先,我们创建一个密码重置请求端点。
@router.post("/password-reset-request")
async def password_reset_request(email_data: EmailSchema):
user_service = UserService()
user = await user_service.get_user_by_email(session, email_data.email)
if not user:
raise HTTPException(status_code=404, detail="User not found.")
reset_token = create_url_safe_token({"email": user.email})
reset_link = f"{config.DOMAIN}/auth/password-reset-confirm/{reset_token}"
html_message = f"<h1>Reset your password</h1><p>Click <a href='{reset_link}'>here</a> to reset your password.</p>"
await send_email([user.email], "Reset your password", html_message)
return {"message": "Password reset link sent. Please check your email."}
接下来,我们创建一个密码重置确认端点。
@router.post("/password-reset-confirm/{token}")
async def password_reset_confirm(token: str, passwords: PasswordResetConfirm, session: AsyncSession = Depends(get_session)):
token_data = decode_url_safe_token(token)
user_email = token_data.get("email")
user_service = UserService()
user = await user_service.get_user_by_email(session, user_email)
if not user:
raise HTTPException(status_code=404, detail="User not found.")
if passwords.new_password != passwords.confirm_new_password:
raise HTTPException(status_code=400, detail="Passwords do not match.")
hashed_password = generate_password_hash(passwords.new_password)
await user_service.update_user(session, user, {"password_hash": hashed_password})
return {"message": "Password reset successfully."}



后台任务


上一节我们介绍了密码重置,本节中我们来看看如何使用后台任务。



首先,我们使用 FastAPI 的内置后台任务功能。




from fastapi import BackgroundTasks





@router.post("/send-email")
async def send_email_endpoint(
email_data: EmailSchema,
background_tasks: BackgroundTasks
):
html_message = "<h1>Welcome to the app</h1>"
background_tasks.add_task(send_email, email_data.addresses, "Welcome", html_message)
return {"message": "Email sent successfully."}








接下来,我们使用 Celery 处理后台任务。









from celery import Celery











celery_app = Celery("tasks", broker=config.REDIS_URL, backend=config.REDIS_URL)
@celery_app.task
def send_email_task(recipients: List[str], subject: str, body: str):
# 发送电子邮件的逻辑
pass
现在,我们可以在路由中调用 Celery 任务。
@router.post("/send-email-celery")
async def send_email_celery_endpoint(email_data: EmailSchema):
html_message = "<h1>Welcome to the app</h1>"
send_email_task.delay(email_data.addresses, "Welcome", html_message)
return {"message": "Email sent successfully."}
API 文档
上一节我们介绍了后台任务,本节中我们来看看如何生成 API 文档。



FastAPI 自动生成 OpenAPI 文档,我们可以通过访问 /docs 或 /redoc 来查看。




我们还可以自定义文档的元数据。


from fastapi import FastAPI




app = FastAPI(
title="Bookly API",
description="A simple book review API.",
version="1.0.0",
contact={
"name": "Your Name",
"email": "your-email@example.com"
},
docs_url="/api/docs",
redoc_url="/api/redoc"
)







部署
上一节我们介绍了 API 文档,本节中我们来看看如何部署应用。








我们将使用 Render 平台部署 FastAPI 应用、PostgreSQL 数据库、Redis 和 Celery 工作器。




部署步骤:
- 创建 Web 服务,指向 FastAPI 应用。
- 创建 PostgreSQL 数据库,并更新环境变量。
- 创建 Redis 实例,用于任务队列。
- 创建 Celery 工作器,处理后台任务。
- 更新环境变量,确保所有服务正确连接。



环境变量示例:
DATABASE_URL=postgresql+asyncpg://user:password@host:port/dbname
REDIS_URL=redis://host:port
MAIL_USERNAME=your-email@gmail.com
MAIL_PASSWORD=your-app-password
DOMAIN=https://your-app.onrender.com










启动命令:
- Web 服务:
fastapi run source - Celery 工作器:
celery -A source.celery_tasks.celery_app worker --loglevel=info
















总结






在本节课中,我们一起学习了如何使用 FastAPI 构建一个完整的 API 应用。我们从获取当前登录用户开始,逐步实现了角色管理、数据库关系、错误处理、中间件、电子邮件发送、密码重置、后台任务、API 文档和部署。通过这些内容,你应该能够构建一个功能齐全的 FastAPI 应用,并将其部署到生产环境中。

浙公网安备 33010602011771号