init medical training project
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import uuid
|
||||
import logging
|
||||
|
||||
from rest_framework.views import exception_handler as drf_exception_handler
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.exceptions import APIException, ValidationError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_STATUS_TO_CODE = {
|
||||
400: 'BAD_REQUEST',
|
||||
401: 'AUTH_UNAUTHORIZED',
|
||||
403: 'PERMISSION_DENIED',
|
||||
404: 'NOT_FOUND',
|
||||
405: 'METHOD_NOT_ALLOWED',
|
||||
429: 'SYS_RATE_LIMIT',
|
||||
500: 'SYS_INTERNAL',
|
||||
503: 'SYS_DEPENDENCY_DOWN',
|
||||
}
|
||||
|
||||
|
||||
def custom_exception_handler(exc, context):
|
||||
trace_id = uuid.uuid4().hex[:12]
|
||||
response = drf_exception_handler(exc, context)
|
||||
|
||||
if response is None:
|
||||
logger.exception('Unhandled server error trace_id=%s', trace_id)
|
||||
return Response(
|
||||
{'code': 'SYS_INTERNAL', 'message': '服务器内部错误', 'details': None, 'trace_id': trace_id},
|
||||
status=500,
|
||||
)
|
||||
|
||||
data = response.data
|
||||
status_code = response.status_code
|
||||
|
||||
# Already structured by AppError or our custom exceptions
|
||||
if isinstance(data, dict) and 'code' in data and 'message' in data:
|
||||
response.data = {**data, 'trace_id': trace_id}
|
||||
return response
|
||||
|
||||
# DRF ValidationError: {'field': ['error msg']}
|
||||
if isinstance(exc, ValidationError):
|
||||
response.data = {
|
||||
'code': 'VALIDATION_ERROR',
|
||||
'message': '请求参数不合法',
|
||||
'details': data,
|
||||
'trace_id': trace_id,
|
||||
}
|
||||
return response
|
||||
|
||||
# Standard DRF exceptions (AuthenticationFailed, PermissionDenied, Throttled, etc.)
|
||||
message = _extract_message(data)
|
||||
response.data = {
|
||||
'code': _STATUS_TO_CODE.get(status_code, 'SYS_INTERNAL'),
|
||||
'message': message,
|
||||
'details': None,
|
||||
'trace_id': trace_id,
|
||||
}
|
||||
return response
|
||||
|
||||
|
||||
def _extract_message(data):
|
||||
if isinstance(data, dict):
|
||||
return str(data.get('detail', data))
|
||||
if isinstance(data, list) and data:
|
||||
return str(data[0])
|
||||
return str(data)
|
||||
|
||||
|
||||
class AppError(APIException):
|
||||
"""统一业务异常。视图中 raise AppError('CODE', '消息', details, status_code) 即可。"""
|
||||
|
||||
def __init__(self, code, message, details=None, status_code=400):
|
||||
self.status_code = status_code
|
||||
super().__init__(detail=code)
|
||||
# 绕过 DRF _get_error_details,防止 None 被转成字符串 "None"
|
||||
self.detail = {'code': code, 'message': message, 'details': details}
|
||||
Reference in New Issue
Block a user