• DRF 02


    APIView

    • APIView继承了View, 并重写了as_view方法
      • 重写的as_view主体上还是View的as_view, 返回的还是view方法
      • 重写的as_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)
    
    • APIView继承了View, 并重写了dispatch方法
    """
    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
    

    请求模块

    • 入口: dispathch---initialize_request---Request
    • 将wsgi的request对象转化为drf的request对象, 并且将wsgi的request赋值给 request._request
    • drf的request对象并没有method方法, 是通过 __getattr__ 反射获取的 _request.method
    • request.query_params 就是 _request.GET, 获取拼接参数
    • request.data 通过 request._full_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
    

    解析模块

    • 入口: dispathch---initialize_request---parsers
    • 解析模块只处理数据包参数: form-data, urlencoded, json
    • 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'
    ],
    
    • 局部配置所有视图类的解析方式, 解析配置可以配置三种
    from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
    
    
    # drf下的视图类继承APIView
    class BookApiView(APIView):
        # 为当前视图类配置解析类
        parser_classes = [JSONParser, FormParser, MultiPartParser]
    
    • 全局配置所有视图类的解析方式, 解析配置可以配置三种
    # 在项目的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'
        ],
    }
    
    
    • 配置的查找顺序:局部(视图类的类属性) => 全局(settings文件的drf配置) => 默认(drf的默认配置)

    响应模块

    • from rest_framework.response import Response
    # data:响应数据
    # status:响应的网络状态码
    # -------------------------
    # template_name:drf完成前后台不分离返回页面,但是就不可以返回data(不许用了解)
    # headers:响应头,一般不规定,走默认
    # exception:一般异常响应,会将其设置成True,默认False(不设置也没事)
    # content_type:默认就是 application/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',
    }
    
  • 相关阅读:
    获得 Web Service 方法的描述信息
    make menuconfig 报错
    汇编调用c函数为什么要设置栈
    UBoot Makefile文件分析
    UBoot启动过程完全分析(转)
    (转)在fedora12下用crosstoolng建立armlinux交叉编译环境
    UBoot编译过程完全分析(转)
    雷军:给互联网创业者的“七字”建议
    uboot根目录下makefile
    Redhat 5 配置Samba服务器
  • 原文地址:https://www.cnblogs.com/bigb/p/12094249.html
Copyright © 2020-2023  润新知