You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI无法返回MongoDB对象的问题求助及报错排查

问题描述

我尝试向以下路由发起请求:
http://127.0.0.1:8000/testadmins
使用如下函数:

@app.get("/testadmins/")
async def get_admins():
    return await get_database()
get_database()函数实现如下:
async def get_database():
    uri = "(my mongo uri)"
    client = MongoClient(uri, server_api=ServerApi('1'))
    adms = client.test['admins'].find()
    print(adms)
    
    return adms

该函数应返回集合中的所有管理员数据。控制台打印admins数据正常,但路由无法返回,出现错误:TypeError('vars() argument must have __dict__ attribute')。我尝试添加async await、用FastAPI的json_encodable()包装函数,但都无效。在Postman中请求返回500(内部服务器错误)。

(更新:我推测问题与日期时间类型有关,因为逐个测试键值时,仅datetime.datetime(...)格式的字段无法正常返回。)
(__v和_id字段也无法正常返回,但不确定原因)

错误追踪信息:

Traceback (most recent call last):
  File "/opt/homebrew/lib/python3.11/site-packages/fastapi/encoders.py", line 235, in jsonable_encoder
    data = vars(obj)
           ^^^^^^^^^
TypeError: vars() argument must have __dict__ attribute

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/homebrew/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/fastapi/applications.py", line 289, in __call__
    await super().__call__(scope, receive, send)
  File "/opt/homebrew/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/opt/homebrew/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/opt/homebrew/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/opt/homebrew/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/opt/homebrew/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/opt/homebrew/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/opt/homebrew/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/opt/homebrew/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/opt/homebrew/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/opt/homebrew/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/fastapi/routing.py", line 291, in app
    content = await serialize_response(
              ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/fastapi/routing.py", line 179, in serialize_response
    return jsonable_encoder(response_content)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/fastapi/encoders.py", line 209, in jsonable_encoder
    jsonable_encoder(
  File "/opt/homebrew/lib/python3.11/site-packages/fastapi/encoders.py", line 238, in jsonable_encoder
    raise ValueError(errors) from e
ValueError: [TypeError("'ObjectId' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
解决方案

问题核心是MongoDB返回的游标、ObjectId、datetime类型无法被FastAPI默认的JSON编码器序列化,具体解决步骤如下:

1. 转换游标为Python列表

find()返回的是MongoDB游标对象,不是可直接序列化的列表,需要先转为列表:

adms = list(client.test['admins'].find())

2. 手动处理特殊字段

MongoDB的_id是ObjectId类型,datetime字段也无法直接转为JSON,可遍历数据逐个处理:

async def get_database():
    uri = "(my mongo uri)"
    client = MongoClient(uri, server_api=ServerApi('1'))
    cursor = client.test['admins'].find()
    adms = []
    for doc in cursor:
        # 将ObjectId转为字符串
        doc['_id'] = str(doc['_id'])
        # 将datetime字段转为ISO格式字符串(替换为你实际的日期字段名)
        if 'create_time' in doc:
            doc['create_time'] = doc['create_time'].isoformat()
        # 不需要__v字段可直接删除
        if '__v' in doc:
            del doc['__v']
        adms.append(doc)
    return adms

3. 使用MongoDB自带的JSON编码器

用bson.json_util工具一键处理所有MongoDB特殊类型:

from bson import json_util
import json

async def get_database():
    uri = "(my mongo uri)"
    client = MongoClient(uri, server_api=ServerApi('1'))
    cursor = client.test['admins'].find()
    adms = list(cursor)
    # 序列化后转回字典,让FastAPI可正常返回
    json_str = json_util.dumps(adms)
    return json.loads(json_str)

4. 全局自定义FastAPI编码器

如果需要全局处理MongoDB类型,可自定义编码器并绑定到FastAPI应用:

from fastapi import FastAPI
import json
from bson import ObjectId
from datetime import datetime

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, ObjectId):
            return str(obj)
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

app = FastAPI()

@app.get("/testadmins/")
async def get_admins():
    data = await get_database()
    return json.loads(json.dumps(data, cls=CustomJSONEncoder))

以上任意一种方式都能解决序列化错误,让路由正常返回MongoDB数据。

内容的提问来源于stack exchange,提问作者Tiron Raul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:44:49