在 Web 开发中,错误是不可避免的(如用户访问了不存在的页面、服务器内部出错、数据格式错误等)。Flask 提供了优雅的错误处理机制,允许你自定义错误响应,而不是向用户展示丑陋的默认报错页面。
良好的错误处理不仅能提升用户体验,还能帮助开发者快速定位问题。
1. 常见的 HTTP 错误码
在处理错误前,先了解几个最常见的状态码:
- 400 Bad Request: 请求语法错误或参数缺失。
- 401 Unauthorized: 未认证(未登录或 Token 无效)。
- 403 Forbidden: 已认证但权限不足。
- 404 Not Found: 请求的资源不存在。
- 500 Internal Server Error: 服务器内部代码出错。
2. 注册错误处理器
使用 @app.errorhandler() 装饰器可以捕获特定的错误码或异常类。
A. 处理特定 HTTP 错误 (如 404, 403)
from flask import render_template, jsonify
# 处理 404 错误
@app.errorhandler(404)
def not_found(error):
# 如果是 API 请求,返回 JSON;如果是网页,返回 HTML
if request.accept_mimetypes.best == 'application/json':
return jsonify({"error": "Resource not found"}), 404
return render_template('errors/404.html'), 404
# 处理 403 错误
@app.errorhandler(403)
def forbidden(error):
return render_template('errors/403.html'), 403B. 处理通用异常 (如 500)
对于 500 错误或其他未捕获的 Python 异常,我们可以注册一个通用的处理器。
@app.errorhandler(500)
def internal_error(error):
# ⚠️ 重要:在 500 错误中,通常需要回滚数据库会话,防止事务锁死
db.session.rollback()
return render_template('errors/500.html'), 500C. 捕获特定 Python 异常
你可以直接捕获 Python 内置异常或自定义异常。
@app.errorhandler(ValueError)
def handle_value_error(error):
return jsonify({"error": "Invalid value provided", "message": str(error)}), 400
@app.errorhandler(Exception)
def handle_generic_exception(error):
# 捕获所有未被其他处理器处理的异常
app.logger.error(f'Unhandled Exception: {str(error)}')
return jsonify({"error": "Internal Server Error"}), 5003. 主动抛出错误
在业务逻辑中,当发现数据不符合要求时,你可以主动抛出 HTTP 异常。
方法一:使用 abort()
这是最常用的方式,它会立即中断当前请求并触发对应的错误处理器。
from flask import abort
@app.route('/user/<int:user_id>')
def get_user(user_id):
user = User.query.get(user_id)
if not user:
abort(404) # 触发 @app.errorhandler(404)
return jsonify(user.to_dict())方法二:返回元组
如果你不想触发全局错误处理器,只想在当前视图返回错误,可以直接返回元组。
@app.route('/api/data')
def get_data():
data = request.get_json()
if not data:
return jsonify({"error": "No data provided"}), 400
# ...4. 蓝图中的错误处理
如果你使用蓝图模块化开发,也可以在蓝图中定义错误处理器。注意:蓝图中的错误处理器只对该蓝图内的路由生效。
# api_bp.py
from flask import Blueprint, jsonify
api_bp = Blueprint('api', __name__)
@api_bp.errorhandler(404)
def api_not_found(error):
return jsonify({"error": "API endpoint not found"}), 404
@api_bp.errorhandler(500)
def api_internal_error(error):
return jsonify({"error": "API internal error"}), 5005. 最佳实践与注意事项
✅ 1. 区分 API 和网页响应
现代应用通常是前后端分离的。建议根据请求头判断返回 JSON 还是 HTML。
def wants_json_response():
return request.accept_mimetypes.best == 'application/json'
@app.errorhandler(404)
def not_found(error):
if wants_json_response():
return jsonify(error="Not Found"), 404
return render_template('404.html'), 404✅ 2. 记录日志 (Logging)
不要吞掉错误!在 500 错误处理器中,务必记录详细的堆栈信息,方便排查 Bug。
import logging
@app.errorhandler(500)
def handle_500(error):
app.logger.exception("An unhandled exception occurred.") # 记录完整堆栈
return "Something went wrong on our end.", 500✅ 3. 数据库回滚
在 500 错误发生时,当前的数据库事务可能处于不一致状态。务必调用 db.session.rollback(),否则后续的请求可能会因为数据库锁或脏数据而失败。
✅ 4. 不要泄露敏感信息
在生产环境中,绝对不要将详细的错误堆栈(Traceback)直接返回给前端用户。这会被黑客利用来攻击你的系统。Flask 默认在 debug=False 时会隐藏详细错误信息,但你仍需在自定义处理器中确保只返回通用的错误提示。
📝 总结模板
这是一个标准的生产级错误处理结构:
from flask import jsonify, render_template
import logging
logger = logging.getLogger(__name__)
@app.errorhandler(404)
def not_found(e):
return jsonify({"code": 404, "msg": "Resource not found"}), 404
@app.errorhandler(400)
def bad_request(e):
return jsonify({"code": 400, "msg": "Bad request"}), 400
@app.errorhandler(500)
def internal_server_error(e):
db.session.rollback() # 回滚数据库
logger.exception("Server Error") # 记录日志
return jsonify({"code": 500, "msg": "Internal server error"}), 500掌握了错误处理,你的 Flask 应用就变得更加健壮和专业了。接下来,你想学习如何部署 Flask 应用到生产环境(如使用 Gunicorn + Nginx),还是学习如何编写单元测试?