from flask import render_template, jsonify, request, flash, redirect, url_for from sqlalchemy.exc import SQLAlchemyError, IntegrityError, OperationalError from werkzeug.exceptions import HTTPException, NotFound, Forbidden, BadRequest import traceback class ErrorHandler: """ 全局异常处理器 用于注册Flask应用程序的错误处理函数 """ def __init__(self, app=None): if app: self.init_app(app) def init_app(self, app): """ 初始化应用程序的错误处理器 :param app: Flask应用实例 """ # 处理SQLAlchemy错误 app.register_error_handler(SQLAlchemyError, self.handle_db_error) app.register_error_handler(IntegrityError, self.handle_integrity_error) app.register_error_handler(OperationalError, self.handle_operational_error) # 处理HTTP错误 app.register_error_handler(NotFound, self.handle_not_found) app.register_error_handler(Forbidden, self.handle_forbidden) app.register_error_handler(BadRequest, self.handle_bad_request) # 处理通用错误 app.register_error_handler(Exception, self.handle_generic_error) # 记录初始化完成 app.logger.info("错误处理器初始化完成") def handle_db_error(self, error): """ 处理一般数据库错误 """ error_details = str(error) traceback_details = traceback.format_exc() current_app = self._get_current_app() current_app.logger.error(f"数据库错误: {error_details}\n{traceback_details}") return self._handle_error("数据库操作错误", "抱歉,数据库操作失败,请稍后重试。", 500) def handle_integrity_error(self, error): """ 处理数据完整性错误(如违反唯一约束) """ error_details = str(error) current_app = self._get_current_app() current_app.logger.error(f"数据完整性错误: {error_details}") # 尝试从错误消息中提取更友好的错误消息 friendly_message = "数据冲突,请检查您的输入。" if "Duplicate entry" in error_details: friendly_message = "数据已存在,请检查您的输入。" return self._handle_error("数据完整性错误", friendly_message, 400) def handle_operational_error(self, error): """ 处理数据库操作错误(如连接问题) """ error_details = str(error) current_app = self._get_current_app() current_app.logger.error(f"数据库操作错误: {error_details}") return self._handle_error("数据库连接错误", "抱歉,无法连接到数据库,请稍后重试。", 503) def handle_not_found(self, error): """ 处理404错误 """ return self._handle_error("页面未找到", "抱歉,您请求的页面不存在。", 404) def handle_forbidden(self, error): """ 处理403错误 """ return self._handle_error("访问被拒绝", "抱歉,您没有权限访问此页面。", 403) def handle_bad_request(self, error): """ 处理400错误 """ return self._handle_error("请求错误", "抱歉,服务器无法理解您的请求。", 400) def handle_generic_error(self, error): """ 处理未被其他处理程序捕获的通用错误 """ error_details = str(error) traceback_details = traceback.format_exc() current_app = self._get_current_app() current_app.logger.error(f"未处理异常: {error_details}\n{traceback_details}") if isinstance(error, HTTPException): return self._handle_error("请求错误", error.description, error.code) return self._handle_error("服务器错误", "抱歉,服务器遇到了未知错误,请稍后重试。", 500) def _handle_error(self, title, message, status_code): """ 根据请求类型返回适当的错误响应 :param title: 错误标题 :param message: 错误消息 :param status_code: HTTP状态码 """ # 根据接受的内容类型决定返回JSON还是HTML # 检查是否是AJAX请求,is_xhr在新版Flask中已被移除 is_ajax_request = request.headers.get('X-Requested-With') == 'XMLHttpRequest' if is_ajax_request or request.headers.get('Accept') == 'application/json': # 返回JSON格式的错误 return jsonify({ 'error': title, 'message': message }), status_code else: # 使用Flash消息并重定向到统计页面 from flask import current_app flash(message) # 根据错误类型决定重定向页面 if status_code == 404: return redirect(url_for('statistics')) elif status_code == 403: return redirect(url_for('statistics')) else: return redirect(url_for('statistics')) def _get_current_app(self): """ 获取当前Flask应用实例 """ from flask import current_app return current_app