FastAPI 基础语法:从一个完整接口理解 Web API 的设计

这篇文章从一个完整的用户接口出发,讲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
相关推荐
Hilaku1 小时前
一行 CSS 新特性干掉 20 行 JavaScript ?
前端·javascript·程序员
Ming_studying1 小时前
Python批量压缩图片:支持JPG_PNG_WebP、尺寸限制与CSV报告
开发语言·图像处理·python·pillow·图片压缩
大熊背1 小时前
ISP图像处理中大数乘法溢出处理(一)
人工智能·python·算法·溢出处理
何以解忧,唯有..1 小时前
Python协程详解:从生成器到async/await的完整指南
开发语言·python
JarvanMo1 小时前
AI 写代码暴增 161 倍,移动开发有没有变得更差?
前端
hhzz1 小时前
智慧校园13类视频异常检测:数据集与预训练模型全攻略
人工智能·pytorch·python·深度学习·目标检测·机器学习
梦曦i1 小时前
RouterLink v2.5.0:H5端原生能力全面回归
前端·uni-app
浩腾数字多媒体1 小时前
如何判断专业电子留言厂家适配条件?
大数据·人工智能·python
小林ixn2 小时前
React + Zustand + JWT:从零实现登录鉴权与请求拦截
前端·react.js·前端框架