Loading

全局异常捕获及源码分析

全局异常捕获

​ drf只会捕获属于drf的异常,所以要做到全局异常捕获,还需要手动操作一下。

​ 经过对drf异常处理组件的源码分析之后可以得知,其实就是自己定义一个exception_handler函数,然后全局替换一下即可

​ 首先要知道,虽然是自己写一个exception_handler函数,但是drf的exception_handler也还是有用的,因为drf已经帮我们处理好了,drf的异常,我们只需要处理其他的异常即可

​ 通过读exception_handler文件可知,只要返回的是None,那么该异常不属于drf

from rest_framework.views import exception_handler, APIView
from rest_framework.response import Response


# 自定义异常类
class PasswordException(Exception):
    def __init__(self, msg):
        self.msg = msg


def common_exception_handler(exc, context):
    # 下面这一堆都是获取日志内容的信息
    request = context['request']
    ip = request.META.get('REMOTE_ADDR')
    path = 'http://127.0.0.1:8000' + request.get_full_path()
    method = request.method
    view = context['view']
    user_id = request.user.pk or '游客登录'
    print(f"请求方式:{method},请求地址:{path},访问ip:{ip},出错地址:{view},用户id:{user_id}")

	# 调用drf内置的exception_handler函数
    res = exception_handler(exc, context)
    # 如果没有res,就说明不是drf的报错
    # 所以需要自己处理,正常返回Response对象,做到真正的全局异常捕获
    if not res:
        return Response({'code': 301, 'message': f'自己的问题哦:>>>{str(exc)}'})
    #
    if isinstance(res.data, dict):
        return Response({'code': 302, 'message': f'drf的问题:>>>{res.data.get("detail")}'})
    return Response({'code': 302, 'message': f'drf的问题:>>>{res.data[0]}'})


异常捕获源码解析

# 首先看dispath
# 这里面首先做了三大认证,然后做了异常捕获处理
def dispatch(self, request, *args, **kwargs):
    self.args = args
    self.kwargs = kwargs
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers 
    try:
        # 这一步是在做三大认证
        self.initial(request, *args, **kwargs)
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

        response = handler(request, *args, **kwargs)

    except Exception as exc:
        # 做了异常捕获
        # 点进handle_exception往下看
        response = self.handle_exception(exc)
		
    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response
# 可以看到做了一堆if判断,最后是要返回一个response
def handle_exception(self, exc):

    if isinstance(exc, (exceptions.NotAuthenticated,
                        exceptions.AuthenticationFailed)):
        
        auth_header = self.get_authenticate_header(self.request)

        if auth_header:
            exc.auth_header = auth_header
        else:
            exc.status_code = status.HTTP_403_FORBIDDEN
	# 点进去看 会发现 get_exception_handler是配置文件中的一个函数 看下面的代码块
    exception_handler = self.get_exception_handler()

    context = self.get_exception_handler_context()
    # 从这里可以看出上面的exception_handler是一个内存地址
    response = exception_handler(exc, context)
	
    # 如果response是None就抛异常 反之正常返回
    if response is None:
        self.raise_uncaught_exception(exc)

    response.exception = True
    return response
def exception_handler(exc, context):
    if isinstance(exc, Http404):
        exc = exceptions.NotFound()
    elif isinstance(exc, PermissionDenied):
        exc = exceptions.PermissionDenied()
	
    # 这一步就是判断 当前的异常是不是属于drf内置的类的对象
    # 如果是 则返回前端可以渲染的 Response
    # 如果不是 则返回None 前端就会爆出错误信息
    if isinstance(exc, exceptions.APIException):
        headers = {}
        if getattr(exc, 'auth_header', None):
            headers['WWW-Authenticate'] = exc.auth_header
        if getattr(exc, 'wait', None):
            headers['Retry-After'] = '%d' % exc.wait

        if isinstance(exc.detail, (list, dict)):
            data = exc.detail
        else:
            data = {'detail': exc.detail}

        set_rollback()
        return Response(data, status=exc.status_code, headers=headers)

    return None

posted @ 2024-04-21 22:02  HuangQiaoqi  阅读(10)  评论(0编辑  收藏  举报