• Django DRF 全局异常处理


    1. 引子

    1. 在三大认证以及视图函数中的错误在源码中会被 try 捕获,如下所示
    def dispatch(self, request, *args, **kwargs):
    	...
        try:
            ...
        except Exception as exc:
            response = self.handle_exception(exc)
    
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 其异常返回的格式已经在 DRF 配置文件中配置了,但是并不符合我们的要求,我们可以重写其方法,看源码可以发现其执行了 handle_exception 方法,该方法源码如下
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. exception_handler = self.get_exception_handler()
      response = exception_handler(exc, context)
      上面俩句源码说明处理的是 get_exception_handler 方法,查看其源码如下
    def get_exception_handler(self):
        """
        Returns the exception handler that this view uses.
        """
        return self.settings.EXCEPTION_HANDLER
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. get_exception_handler 方法中返回异常处理程序。
      这里默认返回的是 settings 下的 EXCEPTION_HANDLER 如下所示
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    
    • 1
    1. 也就是说,我们只要重写 exception_handler 方法即可。

    2. 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    if isinstance(exc, exceptions.APIException): isinstance 方法判断 ecx 是否为 APIException 的实例对象,所有继承了 APIException 的异常都会被处理。也就是说,DRF 只处理自己的异常。REST framework定义的异常如下表格所示

    异常解释
    APIException所有异常的父类
    ParseError解析错误
    AuthenticationFailed认证失败
    NotAuthenticated尚未认证
    PermissionDenied权限决绝
    NotFound未找到
    MethodNotAllowed请求方式不支持
    NotAcceptable要获取的数据格式不支持
    Throttled超过限流次数
    ValidationError校验失败
    1. 注意点:
      drf 默认只处理自己的异常:所有 drf 中抛的异常,都有 detail
      而 django 的异常,抛出很长的 xml 数据

    2. 自定义异常处理

    方法

    from rest_framework.views import exception_handler
    from rest_framework.response import Response
    
    
    def common_exception_handler(exc, context):
        response = exception_handler(exc, context)
        if response:
            res = Response(data={'code': 10001, 'msg': response.data.get('detail')})
        else:
            # res = Response({'code': 10002, 'msg': '服务器内部错误'})
            res = Response({'code': 10002, 'msg': str(exc)})
        return res
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    配置文件

    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'app01.tool.exception.common_exception_handler'
    }
    
    • 1
    • 2
    • 3
    1. 首先编写函数,参数为 exc、context,exc 为错误对象。
    2. 调用原来的 exception_handler 方法,因为直接使用 DRF 对异常的处理,只不过将异常返回的格式改变而已
    3. 进行判断,除了 DRF 的错误还有 Django 的错误,Django 错误的详细信息直接用 exc 表示,但是返回给前端不需要这么详细。在上面我对 Django 和 DRF 的异常 code 区分开来。
    4. 在 setting 配置文件中声明自定义的异常处理,不声明的话默认使用的是 ‘rest_framework.views.exception_handler’

    在自定义异常处理中,还需要记录日志,并且日志越详细越好,例如某个用户在什么时间执行了什么视图函数出现了什么异常以及请求的地址

    在自定义异常处理方法中还有一个参数 context,出现异常的时候打印如下所示

    {
    	'view': <app01.view.ErrorView.Error object at 0x000001EC7F55D940>, 
    	'args': (), 
    	'kwargs': {}, 
    	'request': <rest_framework.request.Request: GET '/test/error/'>
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • context[‘view’] :视图类的对象
    • context[‘request’]:当前请求的对象

    我们可以利用这些属性来生成日志,例如:

    'ip地址为:%s的用户,访问:%s 视图类,报错了,请求地址是:%s'%(request.META.get('REMOTE_ADDR'),str(view),request.path)
    
    • 1
  • 相关阅读:
    一不小心酿成错,做到4点,少上厕所多睡觉,孕期“尿频”别大意
    【云手机】数据安全如何保障?
    【C语言趣味教程】(5) 常量:字面常量 | 类型常量 | const 关键字 | const 的声明 | 程序中的只读概念 | const 保护机制 | 如何巧妙区分 “指针常量“ 和 “常量指针“
    计算机毕业设计ssm+vue基本微信的健康食谱交流 论坛小程序
    Spark Streaming系列-5、应用案例: 百度搜索风云榜
    使用变基整合别人的操作
    MySQL入门(SQL语句、约束)
    pycocotools库的使用
    【FI】FB02中Coding Block字段如何设置为可修改
    深度学习(PyTorch)——torchvision中的数据集使用方法
  • 原文地址:https://blog.csdn.net/m0_58987515/article/details/125413321