Flask 教程

Flask 错误处理

在 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)

纯文本
plaintext
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'), 403

B. 处理通用异常 (如 500)

对于 500 错误或其他未捕获的 Python 异常,我们可以注册一个通用的处理器。

纯文本
plaintext
@app.errorhandler(500)
def internal_error(error):
    # ⚠️ 重要:在 500 错误中,通常需要回滚数据库会话,防止事务锁死
    db.session.rollback() 
    return render_template('errors/500.html'), 500

C. 捕获特定 Python 异常

你可以直接捕获 Python 内置异常或自定义异常。

纯文本
plaintext
@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"}), 500

3. 主动抛出错误

在业务逻辑中,当发现数据不符合要求时,你可以主动抛出 HTTP 异常。

方法一:使用 abort()

这是最常用的方式,它会立即中断当前请求并触发对应的错误处理器。

纯文本
plaintext
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())

方法二:返回元组

如果你不想触发全局错误处理器,只想在当前视图返回错误,可以直接返回元组。

纯文本
plaintext
@app.route('/api/data')
def get_data():
    data = request.get_json()
    if not data:
        return jsonify({"error": "No data provided"}), 400
    # ...

4. 蓝图中的错误处理

如果你使用蓝图模块化开发,也可以在蓝图中定义错误处理器。注意:蓝图中的错误处理器只对该蓝图内的路由生效。

纯文本
plaintext
# 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"}), 500

5. 最佳实践与注意事项

✅ 1. 区分 API 和网页响应

现代应用通常是前后端分离的。建议根据请求头判断返回 JSON 还是 HTML。

纯文本
plaintext
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。

纯文本
plaintext
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 时会隐藏详细错误信息,但你仍需在自定义处理器中确保只返回通用的错误提示。


📝 总结模板

这是一个标准的生产级错误处理结构:

纯文本
plaintext
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),还是学习如何编写单元测试

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注