bigywater.tech
Flask · 10

异常处理器的匹配顺序

同一个异常有好几个处理器都能接,谁先谁后?abort、404、500 是怎么被接住的?蓝图里的和应用里的谁优先?这篇用最小的应用把每种组合都跑一遍。

MRO
处理器按异常类继承关系最近者优先,与注册先后无关
3 层
HTTP 异常的匹配层次:状态码 → HTTPException → Exception(本篇实测)
不属于蓝图
路由未匹配的 404,只走应用级处理器

按类的继承关系匹配

实测 · 本机执行输出
  GET /db   抛 DbError(继承 AppError)   → 500 AppError 处理器 收到 DbError
  GET /app  抛 AppError               → 500 AppError 处理器 收到 AppError
  GET /val  抛 ValueError             → 500 Exception 处理器 收到 ValueError
  (DbError → AppError 处理器,因为 DbError 的 MRO 里 AppError 排在 Exception 前面;反过来先注册 Exception 再注册 AppError 也是这个结果)

上一篇:before_first_request 的变化与替代方案;下一篇:abort() 如何生成错误响应。

HTTP 异常与状态码

实测 · 本机执行输出
  只注册 Exception 处理器                GET /403      → 500 Exception 处理器 收到 Forbidden
  只注册 Exception 处理器                GET /missing  → 500 Exception 处理器 收到 NotFound
  Exception + HTTPException        GET /403      → 403 HTTPException 处理器 收到 Forbidden
  Exception + HTTPException        GET /missing  → 404 HTTPException 处理器 收到 NotFound
  Exception + HTTPException + 403  GET /403      → 403 403 处理器 收到 Forbidden
  Exception + HTTPException + 403  GET /missing  → 404 HTTPException 处理器 收到 NotFound
  没有注册任何处理器                        GET /403      → 403 <!doctype html> <html lang=en> <title>403 Forbidden</title> <h1>Forbid
  没有注册任何处理器                        GET /missing  → 404 <!doctype html> <html lang=en> <title>404 Not Found</title> <h1>Not Fo
  处理第一个请求之后再注册 @errorhandler → AssertionError: The setup method 'errorhandler' can no longer be called on the application. It has already handled its first request, any changes will not be applied 

403 与 Forbidden

实测 · 本机执行输出
  先 403 后 Forbidden: (403, 'second(Forbidden)')
  error_handler_spec 的结构: {None: {403: {'Forbidden': 'second'}}}

蓝图里的处理器

实测 · 本机执行输出
  GET /api/v         → 400 blueprint 的 ValueError 处理器
  GET /v             → 400 应用的 ValueError 处理器
  GET /api/abort404  → 404 blueprint 的 404 处理器
  GET /api/nope      → 404 应用的 404 处理器
  GET /nope          → 404 应用的 404 处理器

500 与 TESTING

实测 · 本机执行输出
  无处理器: 500 <!doctype html> <html lang=en> <title>500 Internal Server Error</title> <h1>Internal Server Error</h1> <p>The server encountered a
  有 @errorhandler(500): (500, '500 处理器:InternalServerError;original_exception = ZeroDivisionError: x/')
  有 @errorhandler(Exception): (500, 'Exception 处理器:ZeroDivisionError')
  TESTING=True 且没有处理器:异常直接传出 test_client → ZeroDivisionError
  TESTING=True 且有 @errorhandler(500):异常直接传出 test_client → ZeroDivisionError
  TESTING=True 且有 @errorhandler(Exception):状态 500,正文 Exception 处理器:ZeroDivisionError

处理器出错与 JSON

实测 · 本机执行输出
  404 处理器里再抛异常 → 500 <!doctype html> <html lang=en> <title>500 Internal Server Error</title> <h1>Internal Serve
  JSON 处理器: 404 application/json {"code":404,"description":"The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.","name":"Not Found"}
  默认 404 页: 404 text/html; charset=utf-8 | <!doctype html> <html lang=en> <title>404 Not Found</title> <h1>Not Found</h1> <p>The requ

分步演示

处理器匹配
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动
    • 没有验证:异步视图里的错误处理器、处理器返回非法类型时的错误信息、Flask 其它版本里 Exception 处理器是否接住 HTTP 异常的差异、blueprint 嵌套时的处理器查找顺序、PROPAGATE_EXCEPTIONS 单独设置的各种组合
    京ICP备2026067502号-1京公网安备11010502063487号