• drf 异常捕获


    异常捕获

       使用drf进行前后端分离时,有的时候当后端出现异常,返回的数据格式并不是JSON,你可能会发现下面这样的情况:

       image-20201101215346205

       它会返回给你一个HTML文档,这显然对于前端开发工程师来说是非常不友好的。

       所以我们通常会将这种情况给他处理掉,学习如何处理之前要看drf是如何对异常进行处理的。

    源码阅读

       首先APIView中的dispatch()方法中有关于异常的捕获,它的异常捕获是从执行认证开始,直到封装返回对象结束。代码如下:

        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  # deprecate?
    
            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:
                response = self.handle_exception(exc)  # 如果发生异常,则执行这里
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    

       在self.handle_exception()这个方法中,你可以看到如下代码:

       def handle_exception(self, exc):
    
            if isinstance(exc, (exceptions.NotAuthenticated,
                                exceptions.AuthenticationFailed)):  # 判断这个异常是不是属于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
    
            exception_handler = self.get_exception_handler()  
    
            context = self.get_exception_handler_context()
            response = exception_handler(exc, context)  # 核心代码就在这里,它会返回这个异常的种类 return self.settings.EXCEPTION_HANDLER。
            你可以导入  from rest_framework.views import exception_handler 来看看它到底是怎么处理的
    
            if response is None:   # 如果这个种类是None时,drf将不再处理这个异常,而是交由Django进行处理,所以你会看见返回大黄页
                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()
    
        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)
    	# ===========以上都是drf能够去处理的异常
        return None 
    

    自定异常

       ok,既然它会将drf没有处理的异常丢给原生Django然后返回大黄页,那么能不能让drf丢给我们写的程序来进行处理呢?

       答案是肯定的。你只需要在全局做一下设置就好了,演示开始。

    全局配置

       首先我们要让发生异常时,第一步就是来运行我们的异常捕获程序,所以要在全局的settings.py中做配置:

    from rest_framework.settings import DEFAULTS
    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': "app01.aberrant.exception_catch"
    }
    

    书写异常

       如果发生drf无法捕获的异常,就由我们来处理,直接返回未知错误以及错误详情即可。

    from rest_framework.views import exception_handler
    from rest_framework.response import Response
    from rest_framework import status
    
    def exception_catch(exc,context):
        # 首先我们做一个切面,拦截掉result。如果它是None则代表会返回大黄页,我们来处理
        result = exception_handler(exc,context)
        if not result:
            # 如果是None,返回未知错误和错误详情,并且返回服务无法使用的状态码
            return Response(data="unkown mistake, details:%s"%(str(exc)),status=status.HTTP_503_SERVICE_UNAVAILABLE)
        return result
    

       下面我们自己造了一个异常,发现了它返回的不是大黄页了:

    class BookAPI(ListAPIView):
        queryset = Book.objects.all()
        serializer_class = BookModelSerializers
        filter_backends = [BookModelSerializers]  # 过滤时放入的是一个序列器,当然会抛出异常
    
    # 返回结果
    "unkown mistake, details:'BookModelSerializers' object has no attribute 'filter_queryset'"
    
  • 相关阅读:
    JavaScript数组迭代方法
    Ant Design Mobile RN中Toast不起作用的原因【坑篇】
    解决vsCode终端不能运行yarn脚本
    k8s——Service和Ingress
    Prometheus学习
    k8s——pod控制器
    k8s——管理pod资源对象
    k8s——资源管理基础
    docker学习
    k8s学习——Helm入门及使用
  • 原文地址:https://www.cnblogs.com/Yunya-Cnblogs/p/13912068.html
Copyright © 2020-2023  润新知