开始
This commit is contained in:
Executable
+150
@@ -0,0 +1,150 @@
|
||||
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
|
||||
Reference in New Issue
Block a user