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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 15:34:55