必须手动注册MongoDB异常处理器,因PyMongo/Motor异常不在FastAPI内置捕获范围内;否则数据库错误将触发500响应、暴露堆栈或返回非结构化响应。需用@app.exception_handler()显式注册ConnectionFailure、OperationFailure等具体类型,并确保所有DB调用路径统一抛出异常。

FastAPI 本身不感知 MongoDB 异常,必须手动注册 MongoException(或具体子类如 ConnectionFailure、ServerSelectionTimeoutError)的处理器,否则数据库错误会直接触发 500 响应并暴露堆栈——这是生产环境最常被忽略的安全盲点。
为什么要单独处理 MongoDB 异常?
PyMongo 和 Motor 抛出的异常不属于 FastAPI 内置捕获范围:HTTPException、RequestValidationError 都不会匹配 ConnectionFailure 或 OperationFailure。默认行为是返回 HTML 错误页(开发模式)或空响应(生产模式),前端收不到结构化 JSON,日志里也看不到明确分类。
-
ConnectionFailure:网络中断、MongoDB 服务宕机 -
ServerSelectionTimeoutError:连接池耗尽、副本集选举中 -
OperationFailure:写入违反唯一索引、聚合管道语法错误 -
InvalidDocument:试图存入不可序列化的 Python 对象(如datetime.timezone实例)
如何注册 MongoDB 异常处理器?
用 @app.exception_handler() 注册具体异常类型,不要用 Exception 兜底——那会把业务逻辑错误和数据库错误混在一起,失去分类价值。
示例(Motor + async):
from motor.motor_asyncio import AsyncIOMotorClient
from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError, OperationFailure
from fastapi import Request
from fastapi.responses import JSONResponse
<p>@app.exception_handler(ConnectionFailure)
@app.exception_handler(ServerSelectionTimeoutError)
async def mongodb_connection_error_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=503,
content={"message": "数据库连接不可用,请稍后重试", "code": "DB_UNAVAILABLE"}
)</p><p>@app.exception_handler(OperationFailure)
async def mongodb_operation_error_handler(request: Request, exc: OperationFailure):</p><h1>根据 error code 细分处理,比如 11000 是重复键</h1><pre class="brush:php;toolbar:false;">if exc.code == 11000:
return JSONResponse(
status_code=400,
content={"message": "数据已存在", "code": "DUPLICATE_KEY"}
)
return JSONResponse(
status_code=400,
content={"message": "数据库操作失败", "code": "DB_OPERATION_FAILED"}
)
- 必须显式导入对应异常类,不能只靠
from pymongo.errors import * - 多个异常共用一个处理器时,用多个
@app.exception_handler()装饰器,不要传元组 - 返回必须是
JSONResponse,不能用return {"message": ...},否则 status_code 会丢失
怎么确保异常真能被捕获到?
常见漏点:异常没在 FastAPI 的请求生命周期内抛出。比如你在 on_event("startup") 里初始化 AsyncIOMotorClient,但连接失败抛的是同步异常,@app.exception_handler 完全收不到。
- 启动时连接检查必须用
try/except包裹,并主动调用client.admin.command("ping") - 所有数据库操作必须在路由函数或依赖项中执行,不能藏在模块级变量初始化里
- 如果用了中间件(如
BaseHTTPMiddleware)做统一 DB 操作,异常要从中间件里向上抛,不能被吞掉 - 异步驱动(Motor)的异常类型和同步驱动(PyMongo)不同,别混用处理逻辑
真正难的不是写 handler,而是让所有数据库调用路径都走同一条异常出口——这要求你禁用任何裸 try/except 在 service 层吞异常,坚持“只 raise,不 catch”。否则,OperationFailure 就可能在某个 create_user 函数里被悄悄转成 ValueError,全局处理器永远等不到它。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











