一个API最容易被忽略的问题就是:

客户端传过来的数据真的可靠吗?

答案通常是否定的。

前端可能传错类型,第三方客户端可能直接构造请求,甚至有人故意发异常数据。

所以后端不能相信客户端传来的任何数据。

最简单的参数校验

假设要创建用户:

@app.post("/users")
async def create_user(
    name: str,
    age: int
):
    ...

FastAPI可以根据类型提示做基础验证。

比如:

age=18

能正常处理。

如果传:

age=abc

就会产生验证错误。

但实际项目里的数据通常比两个参数复杂得多。

使用Pydantic Model

比如:

from pydantic import BaseModel

class UserCreate(BaseModel):
    name: str
    age: int
    email: str

然后:

@app.post("/users")
async def create_user(user: UserCreate):
    return user

请求:

{
  "name": "Tom",
  "age": 18,
  "email": "[email protected]"
}

FastAPI会自动做数据解析和验证。

字段约束

Pydantic不只会检查类型。

还能加约束。

比如:

from pydantic import BaseModel, Field

class UserCreate(BaseModel):
    name: str = Field(min_length=1, max_length=50)
    age: int = Field(ge=0, le=150)

这样:

name

不能为空。

而:

age

必须在合理范围内。

这类校验最好放在数据模型层,而不是散落在业务代码里。

Email校验

Pydantic还可以配合更具体的类型。

比如:

from pydantic import BaseModel
from pydantic.networks import EmailStr

class UserCreate(BaseModel):
    email: EmailStr

这样API收到数据之后能自动检查邮箱格式。

比自己写:

if '@' not in email:
    ...

要可靠得多。

嵌套对象

实际API经常有嵌套结构:

{
  "name": "Tom",
  "profile": {
    "age": 18,
    "city": "Taipei"
  }
}

可以直接定义:

class Profile(BaseModel):
    age: int
    city: str

class UserCreate(BaseModel):
    name: str
    profile: Profile

数据结构一目了然。

请求模型和响应模型分开

我比较建议不要所有接口都复用同一个Model。

比如:

class UserCreate(BaseModel):
    name: str
    password: str

数据库返回:

class UserResponse(BaseModel):
    id: int
    name: str

这样密码不会意外出现在响应里。

这其实是很重要的安全习惯。

别因为:

User

这个模型"看起来能复用",就把数据库对象原样丢给客户端。

Pydantic让接口成为一种契约

前后端开发里一个很烦人的问题就是:

"这个接口到底返回什么?"

如果后端代码里有明确的Pydantic Model,那接口的数据结构就很清楚了。

比如:

class UserResponse(BaseModel):
    id: int
    name: str

前端能明确知道:

id → number
name → string

这其实就相当于建立了一种接口契约。

数据校验应该放在哪

我比较倾向于把不同类型的校验放在不同层。

请求格式
 ↓
Pydantic
 ↓
业务规则
 ↓
Service
 ↓
数据库

比如:

age必须是整数

属于数据格式校验。

而:

用户年龄必须满足某项业务条件

属于业务规则。

两者别完全混在一起。

别只信前端校验

前端当然也应该做校验。

比如:

邮箱格式
密码长度
必填字段

这样用户体验更好。

但后端仍然必须重新校验。

因为:

前端校验 ≠ 安全边界

任何客户端都能绕过前端代码直接调API。

FastAPI + Pydantic的组合

FastAPI和Pydantic放在一起之后,我觉得最大的价值就是:

请求
 ↓
类型解析
 ↓
数据验证
 ↓
业务代码

很多重复的参数检查不用自己手写了。

同时类型定义本身又能成为API文档的一部分。

对于我这种经常要快速写API的场景来说,这种开发体验很舒服。

后面如果继续做FastAPI项目,我也会尽量让API的输入和输出模型保持明确,而不是让各种dict在项目里到处传。

最后修改:2026 年 09 月 02 日
如果觉得我的文章对你有用,请随意赞赏