这篇文章从一个完整的用户接口出发,讲FastAPI 中最核心的基础语法
最终我们实现这样一个接口:
POST /users
客户端提交用户信息:
{
"name": "张三",
"email": "zhangsan@example.com",
"age": 20
}
服务端完成:
请求
↓
Middleware 中间件
↓
Router 路由
↓
Pydantic 参数校验
↓
Service 业务逻辑
↓
SQLAlchemy ORM
↓
MySQL
↓
Response 响应
一、 FastAPI 是什么
FastAPI 本质上是一个 Python Web 框架。
它最核心的工作就是:
HTTP Request
↓
FastAPI
↓
执行你的 Python 代码
↓
HTTP Response
例如:
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def hello():
return {"message": "Hello FastAPI"}
启动之后访问:
GET /hello
得到:
{
"message": "Hello FastAPI"
}
看起来非常简单。
但是这个例子里面实际上已经包含了 FastAPI 最重要的几个概念:
app = FastAPI()
创建应用。
@app.get("/hello")
注册路由。
def hello():
定义接口处理函数。
return {"message": "Hello FastAPI"}
返回 HTTP 响应。
所以可以把 FastAPI 理解成一个"请求分发器"。
客户端发送:
GET /hello
FastAPI 找到:
@app.get("/hello")
然后执行对应函数。
二、一个简易接口
假设现在需要创建用户。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserCreate(BaseModel):
name: str
email: str
age: int
@app.post("/users")
def create_user(user: UserCreate):
return {
"name": user.name,
"email": user.email,
"age": user.age
}
客户端发送:
POST /users
Content-Type: application/json
请求:
{
"name": "张三",
"email": "zhangsan@example.com",
"age": 20
}
FastAPI 会自动完成:
JSON
↓
UserCreate
↓
参数校验
↓
create_user()
这里最值得注意的是:
def create_user(user: UserCreate):
user 并不是一个普通的 Python 字典,而是经过 Pydantic 处理后的对象。
可以直接:
user.name
user.email
user.age
这就是 FastAPI 一个非常重要的设计:
把 HTTP 参数校验直接融入 Python 类型系统。
如果客户端传:
{
"name": "张三",
"email": "xxx",
"age": "abc"
}
FastAPI 会自动进行参数校验。
因此,开发者不需要在每一个接口里面手动写:
if not name:
...
if not email:
...
if not isinstance(age, int):
...
三、请求参数怎么接收
一个真实接口往往不只有 JSON。
HTTP 请求中的参数主要有几种来源:
Path 参数
Query 参数
Body 参数
Header 参数
Cookie 参数
1. Path 参数
例如:
GET /users/100
这里的 100 就是 Path 参数。
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {
"user_id": user_id
}
FastAPI 会自动把:
100
转换成:
int
如果传:
/users/abc
因为 user_id 定义的是:
user_id: int
所以会直接校验失败。
2. Query 参数
例如:
GET /users?page=1&size=10
可以直接写:
@app.get("/users")
def get_users(page: int = 1, size: int = 10):
return {
"page": page,
"size": size
}
FastAPI 会自动识别:
?page=1&size=10
对应:
page
size
3. Body 参数
创建、修改数据时,一般使用 JSON Body。
class UserCreate(BaseModel):
name: str
email: str
age: int
@app.post("/users")
def create_user(user: UserCreate):
...
请求:
{
"name": "张三",
"email": "test@example.com",
"age": 20
}
这种方式非常适合 REST API。
4. Header 参数
例如获取客户端传递的 Token:
from fastapi import Header
@app.get("/profile")
def profile(authorization: str = Header()):
return {
"authorization": authorization
}
请求:
Authorization: Bearer xxx
FastAPI 会把 Header 参数传进来。
所以在实际项目中,我们可以把参数来源理解成:
URL
├── Path
└── Query
HTTP Header
└── Token / Trace ID / Client 信息
HTTP Body
└── JSON / 表单数据
这比单纯记 API 更重要。
四、Pydantic:FastAPI 参数校验的核心
FastAPI 的参数校验主要依赖 Pydantic。
from pydantic import BaseModel, EmailStr
class UserCreate(BaseModel):
name: str
email: EmailStr
age: int
这里已经表达了接口的数据规则:
name → 字符串
email → 邮箱
age → 整数
如果希望增加约束:
from pydantic import BaseModel, EmailStr, Field
class UserCreate(BaseModel):
name: str = Field(min_length=2, max_length=20)
email: EmailStr
age: int = Field(ge=1, le=120)
现在接口实际上已经拥有了一套数据协议。
例如:
{
"name": "a",
"email": "hello",
"age": 200
}
就会被 FastAPI 拦截。
这一点非常重要。
在传统 Web 开发中,经常会出现:
Controller
↓
手动校验参数
↓
调用 Service
而 FastAPI 更倾向于:
HTTP Request
↓
Pydantic
↓
参数合法
↓
Controller
也就是说:
让非法请求尽可能早地失败,而不是进入业务代码以后再处理。
五、ORM:让接口真正连接数据库
前面的接口只是把数据返回出来,并没有真正保存用户。
真实项目肯定需要数据库。
FastAPI 本身并不负责 ORM。
我们通常可以使用:
FastAPI
+
SQLAlchemy
+
MySQL
SQLAlchemy 负责 ORM。
例如定义用户表:
from sqlalchemy import String, Integer
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(
Integer,
primary_key=True
)
name: Mapped[str] = mapped_column(
String(50)
)
email: Mapped[str] = mapped_column(
String(100),
unique=True
)
age: Mapped[int] = mapped_column(
Integer
)
这里的:
class User(Base)
就代表数据库中的:users表。
字段:
id
name
email
age
对应数据库字段。
这样我们就不用直接写:
INSERT INTO users ...
而可以使用 ORM:
user = User(
name=data.name,
email=data.email,
age=data.age
)
db.add(user)
db.commit()
db.refresh(user)
ORM 的核心价值其实不是"少写几条 SQL"。
更重要的是:
把数据库中的数据映射成 Python 对象,让业务代码可以围绕对象进行操作。
六、数据库连接与 Session
ORM 有了以后,还有一个非常重要的问题:
数据库连接从哪里来?
SQLAlchemy 通常需要:
Engine
↓
Session
↓
CRUD
例如:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "mysql+pymysql://root:123456@localhost/test"
engine = create_engine(
DATABASE_URL,
pool_pre_ping=True
)
SessionLocal = sessionmaker(
bind=engine,
autoflush=False,
autocommit=False
)
然后通过依赖注入获取 Session:
from fastapi import Depends
from sqlalchemy.orm import Session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
接口:
@app.post("/users")
def create_user(
user: UserCreate,
db: Session = Depends(get_db)
):
...
这里:
Depends(get_db)
就是 FastAPI 非常重要的:
依赖注入机制。
它实际上在告诉 FastAPI:
执行这个接口之前,先帮我准备一个数据库 Session。
执行完成以后:
finally:
db.close()
自动释放资源。
这就形成了:
请求进入
↓
创建 DB Session
↓
执行接口
↓
提交 / 查询
↓
关闭 Session
这种机制在真实项目中非常重要。
七、Service
如果我们把所有代码都写进去:
@app.post("/users")
def create_user(
user: UserCreate,
db: Session = Depends(get_db)
):
# 参数校验
# 查询用户
# 判断邮箱是否存在
# 创建用户
# 写数据库
# 发送消息
# 记录日志
# 返回结果
项目一大,接口会迅速膨胀。
所以通常会拆成:
Router
Service
Repository / ORM
例如:
router/
user.py
service/
user.py
model/
user.py
schema/
user.py
Router 负责 HTTP:
@router.post("/users")
def create_user(
user: UserCreate,
db: Session = Depends(get_db)
):
return user_service.create_user(db, user)
Service 负责业务:
def create_user(db: Session, data: UserCreate):
exists = db.query(User).filter(
User.email == data.email
).first()
if exists:
raise ValueError("用户已经存在")
user = User(
name=data.name,
email=data.email,
age=data.age
)
db.add(user)
db.commit()
db.refresh(user)
return user
这样一来:
Router
↓
Service
↓
ORM
↓
Database
每一层职责就比较清晰。
FastAPI 负责的是 Web 层,而不是整个项目的架构。
八、Middleware:所有请求都要经过的一层
如果现在需要记录每一个请求的耗时。
难道每个接口都写:
start = time.time()
# 业务代码
print(time.time() - start)
显然不合理。
这种"所有请求都需要执行"的逻辑,就非常适合放到 Middleware。
例如:
import time
from fastapi import Request
@app.middleware("http")
async def log_request(request: Request, call_next):
start = time.time()
response = await call_next(request)
duration = time.time() - start
print(
request.method,
request.url.path,
duration
)
return response
请求:
POST /users
执行过程:
Request
↓
Middleware
↓
Router
↓
Service
↓
ORM
↓
Database
↓
Response
↓
Middleware
↓
Client
所以 Middleware 很适合处理:
日志
Trace ID
耗时统计
跨域
统一 Header
认证前置逻辑
异常处理
但是也不要什么东西都塞进去。
例如:
创建用户
查询用户
修改用户
这些属于业务逻辑,就不应该放 Middleware。
一个简单的判断方式是:
如果这段逻辑和具体业务无关,但又需要影响大量请求,就考虑 Middleware。
九、Server:FastAPI 应用到底是怎么跑起来的
写完:
app = FastAPI()
并不代表服务已经启动。
FastAPI 通常运行在 ASGI Server 上,例如:
Uvicorn
启动:
uvicorn main:app --host 0.0.0.0 --port 8000
这里:
main
表示:
main.py
而:
app
表示:
app = FastAPI()
所以:
uvicorn main:app
实际上就是告诉 Uvicorn:
加载
main.py中的app对象,并把它作为 ASGI 应用运行。
完整链路可以理解成:
浏览器 / 前端
↓
HTTP
↓
Uvicorn
↓
FastAPI
↓
Middleware
↓
Router
↓
Service
↓
SQLAlchemy
↓
MySQL
FastAPI ≠ Uvicorn
FastAPI 是 Web 框架。
Uvicorn 是 ASGI Server。
类似于:
FastAPI
负责:应用逻辑
Uvicorn
负责:把应用真正跑起来、接收 HTTP 请求
生产环境中还可能进一步使用:
Nginx
↓
Uvicorn
↓
FastAPI
Nginx 负责反向代理、HTTPS、静态资源等。
十、把这些东西真正组合成一个完整接口
现在把前面的内容全部串起来。
一个比较典型的项目结构:
app/
├── main.py
├── database.py
├── models.py
├── schemas.py
├── service.py
└── router.py
database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "mysql+pymysql://root:123456@localhost/test"
engine = create_engine(
DATABASE_URL,
pool_pre_ping=True
)
SessionLocal = sessionmaker(
bind=engine,
autoflush=False,
autocommit=False
)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
models.py
from sqlalchemy import String, Integer
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(
Integer,
primary_key=True
)
name: Mapped[str] = mapped_column(
String(50)
)
email: Mapped[str] = mapped_column(
String(100),
unique=True
)
age: Mapped[int] = mapped_column(
Integer
)
schemas.py
from pydantic import BaseModel, EmailStr, Field
class UserCreate(BaseModel):
name: str = Field(
min_length=2,
max_length=20
)
email: EmailStr
age: int = Field(
ge=1,
le=120
)
class UserResponse(BaseModel):
id: int
name: str
email: EmailStr
age: int
service.py
from sqlalchemy.orm import Session
from models import User
from schemas import UserCreate
def create_user(
db: Session,
data: UserCreate
):
exists = db.query(User).filter(
User.email == data.email
).first()
if exists:
raise ValueError("用户已经存在")
user = User(
name=data.name,
email=data.email,
age=data.age
)
db.add(user)
db.commit()
db.refresh(user)
return user
router.py
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from database import get_db
from schemas import UserCreate, UserResponse
from service import create_user
router = APIRouter()
@router.post(
"/users",
response_model=UserResponse
)
def create_user_api(
data: UserCreate,
db: Session = Depends(get_db)
):
return create_user(db, data)
main.py
import time
from fastapi import FastAPI, Request
from router import router
app = FastAPI()
app.include_router(router)
@app.middleware("http")
async def request_logger(
request: Request,
call_next
):
start = time.time()
response = await call_next(request)
duration = time.time() - start
print(
f"{request.method} "
f"{request.url.path} "
f"{duration:.3f}s"
)
return response
然后启动:
uvicorn main:app --host 0.0.0.0 --port 8000
调用:
POST http://localhost:8000/users
Body:
{
"name": "张三",
"email": "zhangsan@example.com",
"age": 20
}
整个请求真正经历的是:
Client
│
▼
HTTP Request
│
▼
Uvicorn
│
▼
FastAPI App
│
▼
Middleware
│
▼
Router
│
▼
Pydantic
参数校验
│
▼
Service
│
▼
SQLAlchemy
│
▼
MySQL
│
▼
User Object
│
▼
Response Model
│
▼
JSON Response
@app.post()
解决的是路由。
BaseModel
解决的是数据模型和参数校验。
Depends()
解决的是依赖注入。
Middleware
解决的是请求级公共逻辑。
SQLAlchemy
解决的是数据库 ORM。
Uvicorn
负责把整个应用真正运行起来。
建立下面这个模型:
FastAPI 项目
Client
│
▼
Uvicorn
│
▼
Middleware
│
▼
Router
│
┌────────┴────────┐
▼ ▼
Pydantic Depends
参数校验 依赖注入
│ │
└────────┬────────┘
▼
Service
│
▼
SQLAlchemy
│
▼
MySQL