如何为FastAPI所有子应用统一处理自定义异常?
问题背景
我有一个包含多个子应用的FastAPI项目(示例仅包含一个子应用),代码如下:
main_app = FastAPI() class CustomException(Exception): def __init__(self, message: str, status_code: int, name: str = "Exception"): Exception.__init__(self) self.name = name self.status_code = status_code self.message = message @main_app.exception_handler(CustomException) async def custom_exception_handler(exception: CustomException) -> JSONResponse: return JSONResponse( status_code=exception.status_code, content={"error": exception.message} ) main_app.mount("/subapp", subapp1)
我已在主应用中处理了异常,但未在subapp1中处理。当在subapp1中抛出CustomException时:
raise CustomException( status_code=status.HTTP_404_NOT_FOUND, message=f"{self.model.__name__} not found", )
出现错误:
RuntimeError: Caught handled exception, but response already started.
可见在子应用中抛出CustomException时,无法被主应用的异常处理器处理,请问如何使用主应用的异常处理器统一处理所有子应用的异常?
解决方案
FastAPI通过mount挂载的子应用拥有独立的异常处理机制,主应用的异常处理器不会自动作用于子应用。要实现统一处理,有两种可行方式:
方式一:将主应用的异常处理器注册到所有子应用
先抽离出独立的异常处理器函数,再分别注册到主应用和所有子应用:
from fastapi import Request, JSONResponse # 定义通用的异常处理器 async def custom_exception_handler(request: Request, exception: CustomException) -> JSONResponse: return JSONResponse( status_code=exception.status_code, content={"error": exception.message} ) # 注册到主应用 main_app.add_exception_handler(CustomException, custom_exception_handler) # 注册到子应用 subapp1.add_exception_handler(CustomException, custom_exception_handler) # 如有更多子应用,重复上述注册步骤即可
注意:异常处理器必须包含request参数——FastAPI要求异常处理器的第一个参数为Request对象,你之前的代码遗漏了该参数,这也是触发RuntimeError的核心原因之一。
方式二:通过中间件让主应用接管子应用异常
如果不想逐个给子应用注册处理器,可以自定义中间件捕获子应用的异常,重新抛出后交由主应用的处理器处理:
from fastapi import Request from starlette.middleware.base import BaseHTTPMiddleware class SubAppExceptionMiddleware(BaseHTTPMiddleware): def __init__(self, app): super().__init__(app) async def dispatch(self, request: Request, call_next): try: return await call_next(request) except CustomException as exc: # 直接将异常抛回给主应用处理 raise exc # 如需处理其他异常,可添加额外except分支 # 给子应用添加中间件 subapp1.add_middleware(SubAppExceptionMiddleware) # 挂载子应用到主应用 main_app.mount("/subapp", subapp1)
这种方式通过中间件拦截子应用的异常,避免了重复注册处理器的操作,适合子应用数量较多的场景。
关键注意事项
- 异常处理器必须包含
request参数,否则FastAPI调用时会出现参数不匹配,导致响应启动后再抛出异常,触发RuntimeError。 - 挂载的子应用是独立的ASGI实例,主应用的全局配置(包括异常处理器)不会自动继承,必须通过显式注册或中间件接管的方式实现统一处理。
内容的提问来源于stack exchange,提问作者Ghasem
相关产品推荐
相关产品推荐

