很多开发者在刚开始使用 FastAPI 时会遇到一个典型问题:精心编写的接口返回了一个自定义对象,但浏览器或客户端收到的却是一堆 “not JSON serializable” 这样的错误信息,而不是预期的 JSON 数据。代码逻辑看起来完全正确,问题究竟出在哪里?本文将带你深入理解 FastAPI 处理响应的底层逻辑,并详细讲解五种核心的对象转 JSON 方法,帮助你彻底解决这一难题。
为什么FastAPI需要进行JSON序列化?
在探讨解决方案之前,有必要先理解其背后的原理。FastAPI 的底层基于 Starlette 框架,当接口需要返回数据时,框架必须将这些数据转换为标准的 HTTP 响应。由于 HTTP 协议传输的是文本(字符串)数据,而我们在 Python 中常用的字典、类实例、Pydantic 模型等都是内存中的对象,并非字符串。因此,必须有一个转换过程,将这些对象“序列化”成 JSON 格式的字符串,才能通过网络发送给客户端。
FastAPI 的强大之处在于,它内置了智能的自动序列化机制。只要你掌握了正确的“返回”方法,在绝大多数情况下,你完全不需要手动进行 json.dumps() 这样的操作,框架会帮你优雅地完成转换。理解并善用这套机制,是高效使用 FastAPI 进行 Web开发 的关键一步。
五种将对象转为JSON的核心方法
下面我们逐一拆解五种最常用、最有效的序列化方案,你可以根据不同的开发场景进行选择。
方法一:使用Pydantic模型(官方推荐)
这是最符合 FastAPI 设计哲学,也是官方最为推崇的方式。通过定义 Pydantic 模型,你不仅能获得自动的 JSON 序列化能力,还能同时享受到数据验证、类型提示和自动 API 文档生成等诸多好处。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
name: str
email: str
@app.get("/user", response_model=User)
def get_user():
# 直接返回Pydantic模型实例,FastAPI会自动将其序列化为JSON
return User(id=1, name="张三", email="zhangsan@example.com")
优点:
- ✅ 自动数据验证:确保传入和传出的数据符合模型定义的约束。
- ✅ 自动生成API文档:
Swagger UI 和 ReDoc 会直接使用模型定义来展示请求/响应结构。
- ✅ 类型安全:配合
IDE 或类型检查工具,能极大提升开发效率和代码健壮性。
方法二:直接返回字典(适用于简单场景)
对于结构简单、无需复杂校验的临时数据,直接返回一个 Python 字典是最快捷的方式。FastAPI 会自动识别并将其序列化为 JSON。
@app.get("/simple")
def get_simple_data():
return {
"code": 200,
"message": "成功",
"data": {"id": 1, "name": "产品A"}
}
方法三:使用JSONResponse(需要精细控制响应时)
当你的接口需要设置自定义的 HTTP 状态码、响应头,或者 Cookie 时,JSONResponse 类提供了最直接的控制能力。
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/custom")
def get_custom_response():
content = {
"status": "success",
"timestamp": "2024-01-15T10:30:00",
"items": [1, 2, 3, 4, 5]
}
return JSONResponse(
content=content,
status_code=200,
headers={"X-Custom-Header": "my-value"}
)
适用场景:需要精确控制响应的 HTTP 元信息时。
方法四:自定义JSON编码器(处理复杂数据类型)
当你返回的数据中包含 datetime、Decimal 或自定义类等 Python 标准 json 库无法直接序列化的类型时,就需要自定义编码器来告诉框架如何转换它们。
from datetime import datetime
from decimal import Decimal
from fastapi import FastAPI
from fastapi.responses import JSONResponse
import json
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, Decimal):
return float(obj)
return super().default(obj)
app = FastAPI()
@app.get("/complex")
def get_complex_data():
data = {
"created_at": datetime.now(),
"price": Decimal("99.99"),
"name": "高级套餐"
}
# 手动使用自定义编码器序列化
return JSONResponse(
content=json.loads(json.dumps(data, cls=CustomEncoder))
)
方法五:ORM模型到JSON的转换(数据库查询场景)
在使用 SQLAlchemy、Tortoise-ORM 等 ORM 工具时,直接从数据库查询出来的模型对象通常也不能直接序列化。最佳实践是结合 Pydantic 的 response_model,让 FastAPI 自动完成转换。
from sqlalchemy.orm import Session
from fastapi import Depends
# 假设已定义User Pydantic模型和UserModel ORM模型
@app.get("/users", response_model=list[User])
def get_users(db: Session = Depends(get_db)):
users = db.query(UserModel).all()
# 由于指定了response_model,FastAPI会自动将ORM对象列表转换为Pydantic模型列表并序列化
return users
常见问题与解决方案
在实际开发中,你可能会遇到以下几个典型问题:
❌ 问题1:返回对象时报错 “Object of type X is not JSON serializable”
- 原因:返回的对象中包含了无法被默认
JSON 编码器处理的类型,如 datetime、自定义类等。
- 解决:采用 方法四(自定义编码器),或者更优的做法是,在返回前将数据转换为 方法一(Pydantic模型)。你可以在
Pydantic 模型的 Config 中配置 json_encoders 来统一处理特定类型的序列化。
❌ 问题2:返回的JSON字段顺序与模型定义不符
❌ 问题3:中文字符在JSON中显示为Unicode编码(如\uXXXX)
- 解决:在使用
JSONResponse 时,设置 ensure_ascii=False 参数。
return JSONResponse(
content=data,
ensure_ascii=False # 允许非ASCII字符(如中文)原样输出
)
FastAPI 默认的序列化行为通常已经处理好了中文,如果遇到问题可检查此项。

实践总结与建议
根据多年项目经验,对于 FastAPI 中的 JSON 序列化,建议遵循以下原则:
- 首选Pydantic模型:它能覆盖90%以上的应用场景,并提供数据验证、类型安全和自动化文档等附加价值,是开发现代化、可维护
API 的基石。
- 统一响应格式:为你的
API 定义一个标准的响应包装模型(例如 {"code": 0, “data“: …, “message“: “…”}),并在所有接口中通过 response_model 统一使用,这能让客户端处理起来更加一致。
- 集中处理特殊类型:在
Pydantic 模型的 Config 中配置 json_encoders,而不是在业务代码中到处编写转换逻辑。
- 善用依赖注入:将与数据库交互、数据转换等逻辑封装到依赖函数中,让路径操作函数专注于处理业务逻辑并返回清晰的
Pydantic 模型。

总而言之,FastAPI 在 JSON 序列化方面提供了强大而灵活的机制。理解 HTTP 响应需要文本字符串这一根本原因后,再掌握上述五种方法及其适用场景,你就能从容应对开发中遇到的大部分数据返回问题。合理运用 Pydantic 模型和 response_model,不仅能避免序列化错误,更能显著提升 API 的健壮性和开发体验。如果你在实践过程中有其他独特的技巧或遇到了新的问题,欢迎在技术社区分享与交流。