Files
4an-workorder/app/utils/error_handlers.py
T
2025-08-08 10:38:10 +08:00

150 lines
5.8 KiB
Python
Executable File

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