FastAPI默认JSON编码器不支持UUID和datetime,需通过BaseModel的json_encoders或jsonable_encoder的custom_encoder统一处理。推荐UUID转str、datetime用isoformat()并注意时区与精度。

FastAPI默认JSON编码器不支持UUID和datetime
FastAPI底层用的是json.dumps(),而Python标准库的json模块原生不识别uuid.UUID和datetime对象,直接返回TypeError: Object of type UUID is not JSON serializable或类似错误。这不是FastAPI的bug,而是Python JSON序列化的固有限制。
你不需要重写整个响应流程,也不必在每个路由里手动str()或.isoformat()——FastAPI提供了统一接管点。
用json_encoders在BaseModel中声明字段级编码规则
这是最常用、最安全的方式,适用于Pydantic v1(FastAPI 0.104.x及更早)或v2中仍使用BaseModel定义响应模型的场景。它只影响该模型的序列化行为,不影响全局或其他模型。
-
json_encoders是BaseModel.Config(v1)或model_config(v2)里的字典,键为类型,值为转换函数 - 对
UUID,推荐直接转str;对datetime,建议用.isoformat()而非strftime,避免时区丢失 - 注意:v2中需写成
model_config = {"json_encoders": {...}},且json_encoders仅对model_dump(mode="json")生效(FastAPI自动调用)
from pydantic import BaseModel
from uuid import UUID
from datetime import datetime
<p>class Item(BaseModel):
id: UUID
created_at: datetime</p><pre class='brush:python;toolbar:false;'># Pydantic v2 写法
model_config = {
"json_encoders": {
UUID: str,
datetime: lambda dt: dt.isoformat(),
}
}
立即学习“Python免费学习笔记(深入)”;
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
全局替换jsonable_encoder的custom_encoder参数
当你无法修改模型定义(比如用第三方库的BaseModel子类),或需要统一处理所有响应中的日期/UUID,可以用FastAPI内置的jsonable_encoder增强逻辑。它在JSONResponse、Response和依赖注入的BackgroundTasks等场景下都会被调用。
- 在路由函数中显式调用
jsonable_encoder(..., custom_encoder={...})是最可控的方式 - 若想全局生效,可封装一个自定义
JSONResponse子类,在render()里预置custom_encoder - 不要覆盖
fastapi.encoders.jsonable_encoder本身——它被多处内部调用,改了可能破坏OpenAPI文档生成
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from uuid import UUID
from datetime import datetime
<p>app = FastAPI()</p><p>@app.get("/item")
def get_item():
data = {"id": UUID("a1b2c3d4-5678-90ab-cdef-1234567890ab"), "created_at": datetime.now()}
encoded = jsonable_encoder(
data,
custom_encoder={
UUID: str,
datetime: lambda dt: dt.replace(microsecond=0).isoformat() + "Z",
}
)
return JSONResponse(content=encoded)
注意时区与精度陷阱
很多人忽略datetime序列化后是否保留时区信息,以及微秒精度是否必要。FastAPI默认不处理时区,.isoformat()对naive datetime不带Z或+00:00,前端解析容易出错。
- 后端存
datetime建议统一用datetime.timezone.utc,输出时强制.astimezone(timezone.utc) - UUID转
str没问题,但别用repr()或hex——前者带UUID('...')前缀,后者丢掉分隔符 - 如果API要兼容旧系统,日期格式必须严格匹配(如
"2024-01-01T12:00:00+00:00"),就别依赖lambda dt: dt.isoformat(),改用dt.strftime("%Y-%m-%dT%H:%M:%S%z")并手动补冒号
真正麻烦的不是怎么转,而是转完之后前后端对“时间”和“唯一标识”的语义理解是否一致。这点在跨团队协作时最容易被跳过。

















