68 lines
2.8 KiB
Python
68 lines
2.8 KiB
Python
from rest_framework.views import exception_handler
|
|
from rest_framework.response import Response
|
|
from rest_framework import status
|
|
import logging
|
|
import traceback
|
|
|
|
from django.conf import settings
|
|
|
|
logger = logging.getLogger('django.request')
|
|
|
|
|
|
def custom_exception_handler(exc, context):
|
|
"""
|
|
统一异常处理:所有错误写入终端日志(logger),生产环境响应不泄露细节。
|
|
"""
|
|
response = exception_handler(exc, context)
|
|
request = context.get('request')
|
|
path = getattr(request, 'path', 'unknown')
|
|
method = getattr(request, 'method', 'unknown')
|
|
user = request.user if request else None
|
|
user_info = f"用户: {user.id if user and user.is_authenticated else '未登录'}"
|
|
|
|
if response is not None:
|
|
logger.error(
|
|
'API异常 path=%s method=%s %s status=%s error=%s',
|
|
path, method, user_info, response.status_code, exc,
|
|
exc_info=exc,
|
|
)
|
|
if settings.DEBUG:
|
|
return response
|
|
error_messages = {
|
|
status.HTTP_400_BAD_REQUEST: {'code': 400, 'message': '请求参数错误'},
|
|
status.HTTP_401_UNAUTHORIZED: {'code': 401, 'message': '请先登录'},
|
|
status.HTTP_403_FORBIDDEN: {'code': 403, 'message': '权限不足'},
|
|
status.HTTP_404_NOT_FOUND: {'code': 404, 'message': '资源不存在'},
|
|
status.HTTP_405_METHOD_NOT_ALLOWED: {'code': 405, 'message': '请求方法不允许'},
|
|
status.HTTP_408_REQUEST_TIMEOUT: {'code': 408, 'message': '请求超时'},
|
|
status.HTTP_429_TOO_MANY_REQUESTS: {'code': 429, 'message': '请求过于频繁'},
|
|
status.HTTP_500_INTERNAL_SERVER_ERROR: {'code': 500, 'message': '服务器内部错误'},
|
|
status.HTTP_502_BAD_GATEWAY: {'code': 502, 'message': '网关错误'},
|
|
status.HTTP_503_SERVICE_UNAVAILABLE: {'code': 503, 'message': '服务暂不可用'},
|
|
status.HTTP_504_GATEWAY_TIMEOUT: {'code': 504, 'message': '网关超时'},
|
|
}
|
|
standard_response = error_messages.get(
|
|
response.status_code,
|
|
{'code': response.status_code, 'message': '请求错误'},
|
|
)
|
|
return Response(standard_response, status=response.status_code)
|
|
|
|
# DRF 未处理的异常(数据库字段缺失、AttributeError 等)→ 500 且无日志是常见问题
|
|
logger.error(
|
|
'未处理API异常 path=%s method=%s %s error=%s traceback=%s',
|
|
path,
|
|
method,
|
|
user_info,
|
|
exc,
|
|
traceback.format_exc(),
|
|
)
|
|
if settings.DEBUG:
|
|
return Response(
|
|
{'code': 500, 'message': str(exc), 'detail': traceback.format_exc()},
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
)
|
|
return Response(
|
|
{'code': 500, 'message': '服务器内部错误'},
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
)
|