怎样处理Python Flask项目中的404错误_自定义errorhandler捕获
@app.errorhandler(404)必须在app.run()前注册,且需显式返回状态码404;若在工厂函数、条件分支或蓝图中错误注册则失效,全局处理须用@app.errorhandler而非@bp.errorhandler。
@app.errorhandler(404) 必须在 app.run() 前注册
Flask 的 @app.errorhandler(404) 不是“写完就生效”的装饰器,它只是标记函数,真正注册发生在应用实例初始化后、app.run() 调用前。如果用工厂函数模式(create_app()),却把 @app.errorhandler(404) 写在函数体外或未被调用的分支里,它根本不会挂载。
常见失效场景:
- 在
if __name__ == '__main__':块里写@app.errorhandler(404)—— 此时app已创建,但装饰器执行时机晚于注册阶段 - 蓝图中定义了
@bp.errorhandler(404),却忘了调用app.register_blueprint(bp) - 错误处理器定义在某个条件判断内(比如
if DEBUG:),而实际运行时条件不满足
404 错误只捕获路由未匹配,不捕获视图内 raise NotFound()
注意:@app.errorhandler(404) 默认只响应「请求路径完全没匹配到任何 @app.route」的情况。它不会捕获你在视图函数里手动 raise NotFound() 或 abort(404) 后又被上层异常处理器吞掉的情形 —— 这类抛出仍会走 @app.errorhandler(404),但前提是异常没被中间件、before_request 钩子或 WSGI 层提前截断。
关键区别:
-
abort(404)→ 触发NotFound异常 → 走@app.errorhandler(404) -
raise Exception('xxx')→ 不是 HTTP 异常 → 不触发@app.errorhandler(404),可能落到@app.errorhandler(500)或默认 500 页面 -
raise NotFound()→ 显式抛出 werkzeug 异常 → 正常触发@app.errorhandler(404)
返回值必须显式带状态码,否则默认 200
很多人写完 @app.errorhandler(404) 函数,返回 JSON 或字符串,结果浏览器看到的是 200 OK 状态,前端逻辑错乱。这是因为 Flask 不会自动把返回值的状态码设为 404 —— 你得自己带上。
正确写法示例:
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
@app.errorhandler(404)
def not_found(error):
return {"error": "resource not found"}, 404 # ← 状态码必须显式写在这里
错误写法:
@app.errorhandler(404)
def not_found(error):
return {"error": "resource not found"} # ← 缺少状态码,响应是 200
其他可接受返回形式:
return render_template('404.html'), 404return jsonify({"error": "not found"}), 404return make_response("Not Found", 404)
蓝图里的 404 处理器只对蓝图子路径生效
如果你注册了蓝图 app.register_blueprint(auth, url_prefix='/auth'),并在 auth 蓝图里写了 @auth.errorhandler(404),那它只捕获 /auth/xxx 这类路径下未匹配的请求,不会覆盖全局 /api/users/123 这样的非蓝图路径。
想统一处理所有 404,必须在主 app 实例上注册;若需分层控制,建议:
- 主 app 注册兜底
@app.errorhandler(404) - 各蓝图按需注册自己的
@bp.errorhandler(404),仅处理该模块内部逻辑级 404(比如用户存在但权限不足,业务上算“找不到”) - 避免在多个地方注册同码处理器,后注册的会覆盖先注册的
404 处理器看似简单,但最容易栽在注册时机、作用域隔离和状态码遗漏这三点上。尤其是多蓝图 + 工厂函数项目,建议在 create_app() 返回前,统一检查所有 errorhandler 是否已通过 app.register_error_handler() 显式调用。