全局异常处理

发布时间 2024-01-02 18:59:52作者: jntmwl

一.全局异常处理

# APIView的dispatch方法中运行了三大认证,然后运行了视图类的方法,如果出了异常,会被异常捕获,捕获后统一处理
    def dispatch(self, request, *args, **kwargs):
        try:
            self.initial(request, *args, **kwargs)
            # Get the appropriate handler method
            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:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    
    
# drf 内置了一个函数,只要上面过程出了异常,就会执行这个函数,这个函数只处理的drf的异常
	-主动抛的非drf异常
    -程序出错了 
    都不会被处理
    我们的目标,无论主动抛还是程序运行出错,都同意返回规定格式--》能记录日志
    公司里一般返回   {code:999,'msg':'系统错误,请联系系统管理员'}
    
    
    
# 写一个函数,内部处理异常,在配置文件中配置一下即可


'第一步:首先在drf的配置文件中有一个配置信息如下(这个后面再配置,先挑出来讲而已)'
# Exception handling
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    '这个配置就是处理drf内部异常的配置文件,如果我们想要进行自定义,就需要在dango的配置文件中自行注册进行替换'
    
'第二步:我们查看dispatch中的异常捕获,发现handle_exception处理了这些异常捕获的信息,而他的参数就是错误信息'

 response = self.handle_exception(exc)
    
'第三步:我们进入他的源码发现他返回的信息是response,并且这个response是由exception_handler方法获得的'


    def handle_exception(self, exc):
        if isinstance(exc, (exceptions.NotAuthenticated,
                            exceptions.AuthenticationFailed)):
            # WWW-Authenticate header for 401 responses, else coerce to 403
            auth_header = self.get_authenticate_header(self.request)

            if auth_header:
                exc.auth_header = auth_header
            else:
                exc.status_code = status.HTTP_403_FORBIDDEN

        exception_handler = self.get_exception_handler()

        context = self.get_exception_handler_context()
        response = exception_handler(exc, context)

        if response is None:
            self.raise_uncaught_exception(exc)

        response.exception = True
        return response

'第四步:我们可以看到他则是来自上面的一行代码'
    
    exception_handler = self.get_exception_handler()
    
'第五步:进入这个get_exception_handler方法的源码,我们可以发现他就是拿了配置文件中的配置返回出去'
    
        def get_exception_handler(self):
        return self.settings.EXCEPTION_HANDLER
    
'而配置信息中对应的是一个函数,因此exception_handler的值就相当于是获取这个函数的结果'
    
    def exception_handler(exc, context):
    if isinstance(exc, Http404):
        exc = exceptions.NotFound()
    elif isinstance(exc, PermissionDenied):
        exc = exceptions.PermissionDenied()

    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
    
    
'研究了源码之后我们就开始自己编写,因为原本的异常处理函数有两个参数,因此我们也需要上两个参数,而它的源码太多了,我们可以跟面向对象的派生方法一样,在我们自行定义的函数内调用原本的函数,在此基础上进行自定义'
    
def common_exception_handler(exc, context):
    # exc 错误对象
    # context:上下文,有view:当前出错的视图类的对象,args和kwargs视图类方法分组出来的参数,request:当次请求的request对象
    # 只要走到这里,就要记录日志 ,只有错了,才会执行这个函数
    # 记录日志尽量详细
    print('时间,登录用户id,用户ip,请求方式,请求地址,执行的视图类,错误原因')
    res = exception_handler(exc, context)
    if res:  # 通过观察原来的函数我们发现有值的时候,说明返回了Response 对象,没有值说明返回None
        # 如果是Response 对象说明是drf的异常,已经被处理了,如果是None表明没有处理,就是非drf的异常
        res = Response(data={'code': 888, 'msg': res.data.get('detail', '请联系系统管理员')}) # 这里就是自定义返回的信息的格式,处理的就是原本的drf中的报错
    else:
        # res = Response(data={'code': 999, 'msg': str(exc)})
        # 记录日志
        res = Response(data={'code': 999, 'msg': '系统错误,请联系系统管理员'})
        # 这里就是处理非drf报错的代码,如果想看具体报错可以把msg的值换成参数中的exc接收的错误信息


    return res


# 在配置文件中配置
REST_FRAMEWORK = {
	'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler',
}