Python 统一异常处理的框架层实现

8次阅读

python框架异常未被捕获的主因是异常未进入请求生命周期主协程/线程,如子线程、未await的asyncio任务、信号处理等场景需手动try/except;自定义异常须继承exception并正确实现__init__,全局handler仅对请求上下文内异常生效。

Python 统一异常处理的框架层实现

Python 异常没被 except 捕获?检查是否绕过了框架层

很多同学在 flask/django/fastapi 里写了全局异常处理器,但 ValueError 或自定义异常还是直接崩掉、返回 500 页面——根本原因不是没写 handler,而是异常压根没传到框架的异常分发路径里。比如你在路由函数里用 Threading.Thread 启了个子线程,里头抛了异常,主线程完全收不到;或者用了 asyncio.create_task() 但没 await,异常就静默丢失。

实操建议:

立即学习Python免费学习笔记(深入)”;

  • 框架层能拦截的异常,仅限于「请求生命周期内、主协程/主线程中同步或 await 过的代码」抛出的异常
  • 子线程、未 await 的 task、信号处理函数、atexit 回调里的异常,必须自己用 try/except 包住并显式记录或 re-raise
  • FastAPI 中,@app.exception_handler() 不会捕获后台任务(BackgroundTasks)里的异常,得在任务函数内部处理

统一捕获所有异常?别直接 except Exception:

写个万能 except Exception: 看似省事,实际会吞掉系统级异常,比如 KeyboardInterrupt(Ctrl+C)、SystemExit,导致服务无法正常退出;还可能掩盖 GeneratorExit asyncio.CancelledError,破坏协程取消语义。

实操建议:

立即学习Python免费学习笔记(深入)”;

  • except BaseException: 更危险,绝对避免
  • 真正要兜底时,只捕获 Exception,并在 finally 前加判断:if not isinstance(e, (KeyboardInterrupt, SystemExit)):
  • Django 中推荐继承 django.core.exceptions.ValidationError 而非裸 Exception,否则可能跳过 Django 自带的表单/模型校验链路

FastAPI 的 @app.exception_handler() 为什么没生效

常见现象:注册了 @app.exception_handler(MyCustomError),但接口里 raise 它,返回的还是默认 json 错误体,甚至 500。最常踩的坑是——你定义的异常类没继承 Exception,或者继承了但没调用父类 __init__,导致 FastAPI 的类型匹配失败。

实操建议:

立即学习Python免费学习笔记(深入)”;

  • 确保自定义异常继承 Exception,且构造函数兼容:class MyError(Exception): def __init__(self, msg): super().__init__(msg)
  • handler 函数签名必须是 def handler(request: Request, exc: MyCustomError) -> Response:,参数名和类型注解缺一不可
  • 如果异常是从依赖(Depends)里抛出来的,handler 一样生效;但若在 lifespan 里抛出,则不会进这个 handler,得靠 try/except 包住整个 lifespan 函数

日志、监控、用户提示怎么分层处理异常

一个异常发生,日志要记、监控要告警、前端要友好提示——但三者关注点不同:日志需要完整 traceback 和上下文变量,监控关心分类和频次,用户只需要“稍后重试”这种话。混在一起处理,要么泄露敏感信息,要么漏掉关键维度。

实操建议:

立即学习Python免费学习笔记(深入)”;

  • 在框架 handler 里,用 Logging.exception() 记原始异常,别只记 str(exc)
  • 对用户返回的错误信息,必须走白名单字段映射,比如 {MyAuthError: "登录已失效,请重新登录"},禁止直接返回 exc.args[0]
  • 监控上报(如 sentry)应放在 handler 最开头,避免后续逻辑出错导致告警丢失;同时设 ignore_errors 过滤掉已知低危异常(如 HTTPException(status_code=404)

异常处理真正的复杂点不在语法,而在于它横跨执行流、线程模型、框架生命周期和可观测性链条。漏掉任意一环,都会让“统一”变成假象。

text=ZqhQzanResources