错误处理

统一处理异常与错误页面,避免把堆栈信息暴露给用户,同时提高代码可维护性。

注册错误处理器

@app.errorhandler 为特定 HTTP 状态码注册处理函数:

from flask import render_template

@app.errorhandler(404)
def not_found(e):
    return render_template("404.html"), 404

@app.errorhandler(500)
def server_error(e):
    # 注意:DEBUG 模式下 500 会显示调试器而不是走这里
    return render_template("500.html"), 500

处理器必须显式返回状态码(第二个返回值),否则响应会是 200。

主动抛出错误:abort

from flask import abort

@app.get("/item/<int:item_id>")
def item_detail(item_id):
    item = db.session.get(Item, item_id)
    if item is None:
        abort(404, description="Item not found")
    return render_template("item.html", item=item)

abort() 抛出 werkzeug.exceptions.HTTPException,会被对应状态码的错误处理器捕获;description 可在处理器中通过 e.description 读取。

自定义业务异常

为 API 定义携带状态码和消息的异常类,让视图代码保持干净:

class APIError(Exception):
    def __init__(self, message="Bad Request", code=400, payload=None):
        super().__init__(message)
        self.message = message
        self.code = code
        self.payload = payload or {}

@app.errorhandler(APIError)
def handle_api_error(e):
    return {"error": e.message, **e.payload}, e.code

# 视图中直接抛出
@app.post("/orders")
def create_order():
    if not request.get_json(silent=True):
        raise APIError("请求体必须是 JSON", 400)
    ...

捕获所有未处理异常

注册 Exception 级别的处理器作为兜底,但要放行 HTTP 异常:

from werkzeug.exceptions import HTTPException

@app.errorhandler(Exception)
def handle_unexpected(e):
    if isinstance(e, HTTPException):
        return e                       # 404/405 等按原样返回
    app.logger.exception("Unhandled exception")  # 记录完整堆栈
    return {"error": "Internal Server Error"}, 500

API 与页面的内容协商

同一个应用同时有页面和 API 时,根据请求偏好返回 HTML 或 JSON:

from flask import request

@app.errorhandler(404)
def not_found(e):
    if request.accept_mimetypes.best == "application/json" or request.path.startswith("/api/"):
        return {"error": "Not Found"}, 404
    return render_template("404.html"), 404

蓝图级错误处理

蓝图可以注册只作用于自己的处理器:

api = Blueprint("api", __name__, url_prefix="/api")

@api.errorhandler(ValueError)
def handle_value_error(e):
    return {"error": str(e)}, 400

注意:404/405 这类由路由系统在进入蓝图之前抛出的错误,只能由应用级处理器捕获,蓝图级的 errorhandler(404) 不会生效。

与日志结合

  • 500 级错误务必用 app.logger.exception() 记录完整堆栈(参见日志章节)。
  • 4xx 一般是客户端问题,记录为 warning 或不记录,避免日志噪音。