FastAPI自定义异常处理器报错:'SnapResponse'对象不可调用
问题描述
在Python 3.10 + FastAPI 0.95.0项目中,定义了如下自定义异常处理器:
async def too_much_face_exception_handler(request, exc): resp = SnapResponse(result="",resultCode="TOO_MUCH_FACE") return resp
触发该异常处理器时,出现如下错误:
ERROR: Exception in ASGI application Traceback (most recent call last): File "/opt/homebrew/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__ await self.app(scope, receive, sender) File "/opt/homebrew/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__ raise e File "/opt/homebrew/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ await self.app(scope, receive, send) File "/opt/homebrew/lib/python3.10/site-packages/starlette/routing.py", line 718, in __call__ await route.handle(scope, receive, send) File "/opt/homebrew/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle await self.app(scope, receive, send) File "/opt/homebrew/lib/python3.10/site-packages/starlette/routing.py", line 66, in app response = await func(request) File "/opt/homebrew/lib/python3.10/site-packages/fastapi/routing.py", line 237, in app raw_response = await run_endpoint_function( File "/opt/homebrew/lib/python3.10/site-packages/fastapi/routing.py", line 163, in run_endpoint_function return await dependant.call(**values) File "/Users/xiaoqiangjiang/source/reddwarf/backend/chat-server/src/routers/snap/photo_process.py", line 23, in photo_upload snap_file = process_snap.process_image(save_dirs + file_uniq_name, file) File "/Users/xiaoqiangjiang/source/reddwarf/backend/chat-server/src/service/snap/photo/process_snap.py", line 56, in process_image reg_face(img_full_path) File "/Users/xiaoqiangjiang/source/reddwarf/backend/chat-server/src/tool/graphics/recognize.py", line 29, in reg_face raise TooMuchFaceException(); src.common.exception.snap.too_much_face_exception.TooMuchFaceException: too much face During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/opt/homebrew/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 436, in run_asgi result = await app( # type: ignore[func-returns-value] File "/opt/homebrew/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__ return await self.app(scope, receive, send) File "/opt/homebrew/lib/python3.10/site-packages/fastapi/applications.py", line 276, in __call__ await super().__call__(scope, receive, send) File "/opt/homebrew/lib/python3.10/site-packages/starlette/applications.py", line 122, in __call__ await self.middleware_stack(scope, receive, send) File "/opt/homebrew/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__ raise exc File "/opt/homebrew/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__ await self.app(scope, receive, _send) File "/opt/homebrew/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 91, in __call__ await response(scope, receive, sender) TypeError: 'SnapResponse' object is not callable
SnapResponse和RestResponse定义如下:
# SnapResponse from src.models.chat.rest_response import RestResponse class SnapResponse(RestResponse): def __init__(self, result=None, resultCode=None, uploaded=None): super().__init__(result=result, resultCode=resultCode) self.uploaded = uploaded
# RestResponse class RestResponse: def __init__(self, msg="", result=None, resultCode="200", statusCode="200"): self.msg = msg self.result = result if result else {} self.resultCode = resultCode self.statusCode = statusCode def to_dict(self): return { "msg": self.msg, "result": self.result, "resultCode": self.resultCode, "statusCode": self.statusCode }
请问遗漏了什么配置或实现?该如何修复这个TypeError问题?
问题原因及修复方案
问题根源
FastAPI/Starlette的异常处理器要求返回Starlette/FastAPI官方的Response对象(比如JSONResponse),你当前返回的SnapResponse只是普通Python类实例,并非ASGI兼容的响应对象。Starlette会尝试把返回值当作可调用的ASGI应用执行,因此触发TypeError: 'SnapResponse' object is not callable错误。
修复方法
有两种常用修复方式:
方式一:用JSONResponse包装自定义Response
修改异常处理器,将SnapResponse转为字典后用JSONResponse包裹返回:
from fastapi.responses import JSONResponse async def too_much_face_exception_handler(request, exc): resp = SnapResponse(result="", resultCode="TOO_MUCH_FACE") # 调用to_dict转为字典,再用JSONResponse包装 return JSONResponse(content=resp.to_dict(), status_code=int(resp.statusCode))
方式二:让自定义Response继承官方Response类
修改RestResponse,让它继承自JSONResponse,这样SnapResponse就会成为合法的ASGI响应对象:
from fastapi.responses import JSONResponse class RestResponse(JSONResponse): def __init__(self, msg="", result=None, resultCode="200", statusCode="200", uploaded=None): # 组装响应内容 content = { "msg": msg, "result": result if result else {}, "resultCode": resultCode, "statusCode": statusCode, "uploaded": uploaded } # 调用父类初始化方法,传入内容和状态码 super().__init__(content=content, status_code=int(statusCode)) # SnapResponse无需额外修改,继承自RestResponse即可 class SnapResponse(RestResponse): def __init__(self, result=None, resultCode=None, uploaded=None): super().__init__(result=result, resultCode=resultCode, uploaded=uploaded)
两种方式的选择:如果只是临时处理异常返回,方式一更简单;如果希望自定义Response作为项目标准响应格式,方式二更合适。
内容的提问来源于stack exchange,提问作者spark
相关产品推荐
相关产品推荐

