Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 491|回复: 0

Django-djangorestframework-异常模块-源码及自定义异常

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-11 08:55:11 | 显示全部楼层 |阅读模式

    异常模块

    为什么要自定义异常模块

    1. 所有经过 drf APIView 视图类产生的异常,都可以提供异常处理方案(没有继承 APIVIew 的视图函数不会触发)
    2. drf 默认提供了异常处理方案(rest_framework.views.exception_handler),但是处理范围有限
    3. drf 提供的处理方案有两种
      • 有对应处理,处理了返回异常信息
      • 没有对应处理(处理范围之外),返回 None,直接服务器抛异常给前台
    4. 自定义异常的目的就是解决 drf 没有处理的异常,让前台得到合理的异常信息返回,后台记录异常具体的信息(方便事后排查)

    如果程序报错了,我们应该尽可能的隐藏后台的错误,返回给前台就是服务器错误(你返回给用户用户也看不懂呀,如果是黑客,那可能还会利用报错袭击服务器)

    常见的几种异常情况

    1. 像这种就比较可怕了,甚至连代码文件位置都暴露了

    1. drf 异常处理模块处理后的异常

    1. drf 异常处理模块处理后的异常

    1. 异常信息经汉化后的报错(django 配置了国际化后)

    异常模块源码分析

    视图函数执行出现异常会自动触发 handle_exception 函数

    每个请求都会经历这么一个过程,走到 dispatch 函数

    E:/python3-6-4/Lib/site-packages/rest_framework/views.py 源码

    	# ...
    	def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            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)  # 上面 try 代码体内代码出现异常会自动触发这个函数 <---------
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    

    handle_exception 源码

        def handle_exception(self, exc):
            """
            Handle any exception that occurs, by returning an appropriate response,
            or re-raising the error.
            """
            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()
            # 异常处理的结果
            # 自定义异常就是提供 exception_handler 异常处理函数,处理的目的就是让 response 一定有值
            response = exception_handler(exc, context)
    
            if response is None:
                self.raise_uncaught_exception(exc)  # 乱七八糟的异常就是这里抛出来的
    
            response.exception = True
            return response
    

    如何获取异常类?

    get_exception_handler_context 源码,异常处理类是从配置中拿来的

        def get_exception_handler(self):
            """
            Returns the exception handler that this view uses.
            """
            return self.settings.EXCEPTION_HANDLER
    
        # API policy implementation methods
    

    E:/python3-6-4/Lib/site-packages/rest_framework/settings.py

        # Exception handling
        'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    

    获取到异常类如何处理?

    返回 None 就会触发 handle_exception 源码中的报错

    E:/python3-6-4/Lib/site-packages/rest_framework/views.py drf 自带的异常处理类

    def exception_handler(exc, context):
        """
        Returns the response that should be used for any given exception.
    
        By default we handle the REST framework `APIException`, and also
        Django's built-in `Http404` and `PermissionDenied` exceptions.
    
        Any unhandled exceptions may return `None`, which will cause a 500 error
        to be raised.
        """
        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  # 其他的异常 drf 未处理,返回 None,让其报错(最上面的那种报错)
    
    

    自定义 drf 异常处理

    自定义异常处理模块就是提供 exception_handler 异常处理函数,处理的目的就是让 response 一定有值

    显而易见,我们只需要自定义一个异常处理方法,先调用系统自带的那个异常处理函数,然后把 drf 自带那个异常函数没有处理的情况处理了就好了(处理后返回一个 Response 对象即可,一定要有返回值,否则没多大意义)

    歩鄹

    1. 先将异常处理交给 rest_framework.views 的 exception_handler 去处理
    2. 判断处理的结果(返回值)response,有值代表 drf 已经处理了,None 需要自己处理
      • 可以根据 exc 的类型再细化处理 if isinstance(exc, '哪个异常'): # 再怎么处理

    api/exception.py

    记得自己把报错信息记到日志里面去

    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework.views import Response
    from rest_framework import status
    
    
    def exception_handler(exc, context):
        # drf的exception_handler做基础处理
        response = drf_exception_handler(exc, context)
        # 为空,说明 drf 中没有对应的处理,咱们自定义二次处理
        if response is None:
            # print(exc)
            # # Book matching query does not exist
            
            # print(context)
            # # {'view': <api.views.Book object at 0x000001FED29DD860>}, 'args': (), 'kwargs': {'pk': '4'}, 'request': <rest_framework.request.Request object at 0x000001FED2CD9EF0>
            
            # 这里后期应该写成系统日志才对(这只是演示的伪代码)
            print('%s - %s - %s' % (context['view'], context['request'].method, exc))
            # <api.views.Book object at 0x000002505A2A9A90> - GET - Book matching query does not exits.
            return Response({
                'detail': '服务器错误'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
        return response
    
    

    配置上,让其生效

    dg_proj/settings.py

    # 1.确保已注册 drf
    INSTALLED_APPS = [
    	# ...
        'api.apps.ApiConfig',
    
        'rest_framework',  # 注册 drf
    ]
    
    # 2.在 restframework 的配置中配置该自定义异常模块
    REST_FRAMEWORK = {
        # ...
    
        'EXCEPTION_HANDLER': 'api.exception.exception_handler',  # 全局配置异常模块
    }
    
    
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 16:08 , Processed in 0.060096 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表