zoukankan      html  css  js  c++  java
  • day 73 APIView请求生命周期

    APIView请求生命周期

    • APIView类继承View类,重写了as_view和dispatch方法
    • 重写的as_view方法,主体还是View的as_view,只是在返回视图view函数地址时,局部禁用csrf认证
    # 继承了View
    class APIView(View):
        @classmethod
        def as_view(cls, **initkwargs)
            if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
                def force_evaluation():
                    raise RuntimeError(
                        'Do not evaluate the `.queryset` attribute directly, '
                        'as the result will be cached and reused between requests. '
                        'Use `.all()` or call `.get_queryset()` instead.'
                    )
                cls.queryset._fetch_all = force_evaluation
            
            # 可见, 重写的as_view的主体还是View的as_view
            view = super().as_view(**initkwargs)
            
            view.cls = cls
            view.initkwargs = initkwargs
     
            # 就增加的一个功能, 就是局部禁用crsf认证
            return csrf_exempt(view)
    
    • 重写的dispatch方法
      • 在执行请求逻辑前:请求模块(二次封装request)、解析模块(三种数据包格式的数据解析
      • 在执行请求逻辑中:异常模块(执行出现任何异常交给异常模块处理)
      • 在执行请求逻辑后:响应模块(二次封装request)、渲染模块(响应的数据能在json和页面两种形式进行渲染)
    """
    1.在执行请求逻辑前:
        请求模块(二次封装request), 解析模块(三种数据包格式的数据解析)
    2.在执行请求逻辑中: 
        异常模块(处理执行过程中的任何异常)
    3.在执行请求逻辑后: 
        响应模块(二次封装response), 渲染模块(响应的数据能通过JSON和页面两种形式进行渲染)
    """
    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
            # 1.请求模块和解析模块: initialize_request
            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)
            # 2.异常模块, 捕获并处理异常: handle_exception
            except Exception as exc:
                response = self.handle_exception(exc)
            
            # 3.响应模块和渲染模块: finalize_response
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    

    请求模块

    • 将wsgi的request对象转化成drf的Request类的对象
    • 封装后的request对象完全兼容wsgi的request对象,并且将原request保存在新request._request
    • 重写格式化请求数据存放位置
      • 拼接参数:request.query_params
      • 数据包参数:request.data
    class Request:
        """
        Wrapper allowing to enhance a standard `HttpRequest` instance.
    
        Kwargs:
            - request(HttpRequest). The original request instance.
            - parsers_classes(list/tuple). The parsers to use for parsing the
              request content.
            - authentication_classes(list/tuple). The authentications used to try
              authenticating the request's user.
        """
    
        def __init__(self, request, parsers=None, authenticators=None,
                     negotiator=None, parser_context=None):
          
            # drf的request兼容wsgi的request
            self._request = request
    
                
    
    # drf的Request类下面的__getattr__方法
    def __getattr__(self, attr):
        """
        If an attribute does not exist on this instance, then we also attempt
        to proxy it to the underlying HttpRequest object.
        """
        # drf的request中没有的属性和方法, 会通过反射去wsgi中的request对象中寻找
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)
    
    # 可见, 这个query_params就是_request.GET
    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET
    
    
    # request.data是方法属性, 返回request._full_data的值
    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
            return self._full_data
    

    解析模块

    只处理数据包参数 - form-data, urlencoded, json

    • 全局配置所有视图类的解析方式,解析配置可以配置三种
    # 在项目的settings.py文件中进行如下配置
    
    from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
    
    # drf框架自定义配置
    REST_FRAMEWORK = {
        # 全局配置解析类, 适用于所有视图类
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser',
            'rest_framework.parsers.FormParser',
            'rest_framework.parsers.MultiPartParser'
        ],
    }
    
    • 局部配置当前视图类的解析方式,解析配置可以配置三种
    from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
    
    
    # drf下的视图类继承APIView
    class BookApiView(APIView):
        # 为当前视图类配置解析类
        parser_classes = [JSONParser, FormParser, MultiPartParser]
    
    • 配置的查找顺序:局部(视图类的类属性) =》全局(settings文件的drf配置) =》默认(drf的默认配置)
    # 因此我们的视图类可以自定义parser_classes属性, 来覆盖父类的属性
    class APIView(View):
        # APIView的默认的解析属性
        parser_classes = api_settings.DEFAULT_PARSER_CLASSES
        
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],
    

    注意:该模块仅供了解,但是全局与局部配置是重点

    响应模块

    • data:响应数据
    • status:响应的网络状态码
    • template_name:drf完成前后端不分离返回页面,但是就不可以返回data
    • headers:响应头,一般不规定,默认
    • exception:一般异常响应,会将其设置成True,默认False
    • content_type:默认是 appliacation/json,不需要处理
    class Response(SimpleTemplateResponse):
        """
        An HttpResponse that allows its data to be rendered into
        arbitrary media types.
        """
    
        def __init__(self, data=None, status=None,
                     template_name=None, headers=None,
                     exception=False, content_type=None):
            """
            Alters the init arguments slightly.
            For example, drop 'template_name', and instead use 'data'.
    
            Setting 'renderer' and 'media_type' will typically be deferred,
            For example being set automatically by the `APIView`.
            """
            super().__init__(None, status=status)
    
            if isinstance(data, Serializer):
                msg = (
                    'You passed a Serializer instance as data, but '
                    'probably meant to pass serialized `.data` or '
                    '`.error`. representation.'
                )
                raise AssertionError(msg)
    
            self.data = data
            self.template_name = template_name
            self.exception = exception
            self.content_type = content_type
    
            if headers:
                for name, value in headers.items():
                    self[name] = value
    

    渲染模块

    • Postman请求结果是json, 浏览器请求的结果是页面
    • 同解析模块一样, render渲染模块也可以进行局部配置和全局配置
    • 默认配置
    'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',
        ],
    
    • 局部配置
    # views.py
    from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
    
    
    # drf下的视图类继承APIView
    class BookApiView(APIView):
        
        # 为当前视图类配置渲染类
        renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
    
    • 全局配置
    # settings.py
    REST_FRAMEWORK = {
        # 全局配置渲染类:适用于所有视图类
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',  # 上线后尽量关闭
        ],
    

    异常模块

    • drf默认的异常处理模块只处理客户端请求异常
    # dispatch方法中的异常捕获 
    except Exception as exc:
        response = self.handle_exception(exc)
    
    # APIView下面的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': 'rest_framework.views.exception_handler',
            exception_handler = self.get_exception_handler()
            
            # 给异常处理提供额外的参数
            context = self.get_exception_handler_context()
            
            response = exception_handler(exc, context)  # exc: 异常对象, content: 视图对象和请求对象
    
            if response is None:
                self.raise_uncaught_exception(exc)
    
            response.exception = True
            return response
    
    # 默认的异常处理函数只处理客户端的请求异常
    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
    
    • 自定义异常处理方法
    # 1.在app文件夹下新建exception.py文件
    
    # 2.在exception.py书写自定义异常处理方法
    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework.response import Response
    
    
    def exception_handler(exc, context):
        # 主体还是drf默认的异常处理方法
        response = drf_exception_handler(exc, context)
        
        
        # 按照一定格式返回异常信息
        detail = f"{context.get('view')}-{context.get('request')}-{exc}"
        # 服务端错误
        if not response:
            return Response({
                'detail': detail
            })
    
        else:
            response.data = {'detail': detail}
        return response
    
    
    
    # 3.在项目settings.py文件下进行如下配置
    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'api.exception.exception_handler',
    }
    
  • 相关阅读:
    oracle 查詢表字段明細、字段注釋、表註釋
    Linux Oracle服务启动&停止脚本与开机自启动
    Tomcat7中配置Oracle 11g数据库DBCP连接池
    使用 Tomcat 7 新的连接池 —— Tomcat jdbc pool
    【转】Linux下常用压缩 解压命令和压缩比率对比
    【转】HttpClient容易忽视的细节——连接关闭
    JAVA实现图片验证码
    Flyme密码验证策略升级,忘记锁屏密码及「关机密码」功能
    【转】SpringBoot自定义序列化的使用方式--WebMvcConfigurationSupport
    inux中查看各文件夹大小命令:du -h --max-depth=1
  • 原文地址:https://www.cnblogs.com/colacheng0930/p/12094769.html
Copyright © 2011-2022 走看看