zoukankan      html  css  js  c++  java
  • DRF ---- APIview生命周期 请求/渲染/解析/异常/响应/ 模块源码 drf配置

    drf框架的封装风格

    # 接口视图
    from rest_framework.views import APIView
    # 响应
    from rest_framework.response import Response
    # 请求
    from rest_framework.request import Request
    # 异常
    from rest_framework.exceptions import APIExieption
    # 序列化组件
    from rest_framework.serializers import Serializer
    # 配置
    from rest_framework.settings import APISettings
    # 过滤
    from rest_framework.filters import SearchFilter
    # 分页
    from rest_framework.pagination import PageNumberPagination
    # 用户认证
    from rest_framework.authentication import TokenAuthentication
    # 校验
    from rest_framework.permissions import IsAuthenticated
    # 频率
    from rest_framework.throttling import SimpleRateThrottle
    
    INSTALLED_APPS = [
        'rest_framework', # drf一定要注册 补充:用到了Django的auth表
    ]
    

    1. 原生Django View的源码复习

    # 原生Django view
    from django.view import View # 本质是导入__init__.py 的 generic.base View
    
     # 原生Django View 源码入口  类点了一个方法 点击去as_view
     re_path(r'^test/(?P<pk>)d+',views.Text.as_view()),
    

    as_view源码

    @classonlymethod
        def as_view(cls, **initkwargs):
            # 使用了classonlymethod 默认将当前视图类对象作为第一参数传入
            # 请求-响应流程的主要入口点。
           
            # 一. 是一个闭包函数 获取到了外面的 cls 和 initkwargs
            def view(request, *args, **kwargs):
                # 三. FBV只要有人访问就会自动执行 默认将 wsgi处理的request传入
                # args和kwargs是无名和有名分组自动传的值 {'pk': '111'}
                self = cls(**initkwargs)
                # 使用当前视图类对象 实例化 self
    
                # 同时将参数都封装到self对象中
                self.request = request
                self.args = args
                self.kwargs = kwargs
                # 最后调用 dispatch方法 但是实例化的对象没有 所以找类的 然后父类的 这里的dispatch是父类的
                # 将参数都传入 且返回
                return self.dispatch(request, *args, **kwargs)
    
            # 二. 函数未执行,但是返回 view的内存地址
            return view
    

    dispatch源码

     def dispatch(self, request, *args, **kwargs):
            # 判断 请求是否在http_method_names中
            if request.method.lower() in self.http_method_names:
            # 这里的self是当前类对象 通过反射 请求的函数 如果没有函数的话 返回 http_method_not_allowed
                handler = getattr(self, request.method.lower(),self.http_method_not_allowed)
            else:
                # 如果请求不在里面 http_method_not_allowed函数地址给handler
                handler = self.http_method_not_allowed
            # 最后执视图函数 里面的返回值就是httpresponse
            return handler(request, *args, **kwargs)
    

    2. ApiView的生命周期(源码)

        # 源码入口: as_view() 自己类中没有 继续找父类的 发现父类重写as_view方法
        url(r'^text/(?P<pk>d+)', views.DrfCBV.as_view())
    

    重写的as_view源码

    1 ApiView 继承View类 重写了as_view dispatch方法

    2 重写的as_view方法, 主体还是Viewas_view , 只是在返回视图view函数地址时,禁用了csrf

        @classmethod
        def as_view(cls, **initkwargs):
      
            # 这里没有做什么特别的 直接调用了父类View 的 as_view 拿到的也只是内存地址
            view = super().as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
    
            # 最后只是将内存地址 局部禁用了csrf
            return csrf_exempt(view)
    

    重写的dispatch源码

    1 重写的dispatch方法:

    ​ 在执行请求逻辑前:请求模块(二次封装request)、解析模块(三种数据包格式的数据解析)

    ​ 在执行请求逻辑中:异常模块(执行出现任何异常交给异常模块处理)

    ​ 在执行请求逻辑后:响应模块(二次封装response)、渲染模块(响应的数据能JSON和页面两种渲染)

    def dispatch(self, request, *args, **kwargs):
    	# 接受到的参数一一放入self中 self是当前视图对象
    	self.args = args
    	self.kwargs = kwargs
    	# 二次封装了request 原来的是wsgi的 现在的是restframework
    	# 包含解析模块
    	request = self.initialize_request(request, *args, **kwargs)
    	self.request = request # request
    	self.headers = self.default_response_headers
            
    	try:
    	# 三大认证 (认证, 权限 , 评率) , 用来替换csrf安全认证, 要比csrf认证强大的多
    	self.initial(request, *args, **kwargs)
    	# 判断 请求是否在http_method_names中
    	if request.method.lower() in self.http_method_names:
    	#这里的self是当前类对象 通过反射 请求的函数 如果没有函数的话 返回http_method_not_allowed
    		handler = getattr(self, request.method.lower(),self.http_method_not_allowed)
    	else:
    		# 如果请求不在里面 http_method_not_allowed函数地址给handler
    		handler = self.http_method_not_allowed
    		# 最后执视图函数
    		response = handler(request, *args, **kwargs)
    
    	except Exception as exc:
    		# 异常模块 处理请求异常分支
    		response = self.handle_exception(exc)
    	# 二次封装response, 处理了结果渲染
    	self.response = self.finalize_response(request, response, *args, **kwargs)
    	return self.response
    

    3 . 请求模块

    # 入口 ApiView的 dispatch 的 initialize_request 中
    

    initialize_request 源码

     def initialize_request(self, request, *args, **kwargs):
            
            # 解析的内容 解析数据
            parser_context = self.get_parser_context(request)
    
            # 返回 request对象 类加括号实例化
            # Request 是 from rest_framework.request import Request
            return Request(
                request,
                parsers=self.get_parsers(), # self是APIView类的对象
                authenticators=self.get_authenticators(),
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
    

    Request 源码

    所以需要点进去Request里面查看 干了什么

    class Request:
    	# 传入的参数 自动执行init
        def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
            # 断言 是否是 Htpprequest的实例 
            assert isinstance(request, HttpRequest), (....)
            
            self._request = request # 二次封装request 将原始request作为 drf的_request的属性
            # self.一个属性 走的是 __getattr__方法
            self.parsers = parsers or ()
            self.authenticators = authenticators or ()
            self.negotiator = negotiator or self._default_negotiator()
            self.parser_context = parser_context
            self._data = Empty
            self._files = Empty
            self._full_data = Empty
            self._content_type = Empty
            self._stream = Empty
            ...
    

    Request 下 _getattr 源码

        def __getattr__(self, attr):
            try:
                # 如果点不不存在的方法 那么他就会从 _request反射 出attr
                # 兼容 request
                return getattr(self._request, attr)
            except AttributeError:
                return self.__getattribute__(attr)
    

    总结(重点)

    # 源码分析:
    # 入口:APIVIew的dispatch方法的 request=self.initialize_request(request, *args, **kwargs)
    print(request._request.method)  # 在内部将wsgi的request赋值给request._request
    print(request.method)  # 就是通过__getattr__走的是request._request.method
    print(request.query_params)  # 走的是方法属性,就是给request._request.GET重新命名
    print(request.data)  # 走的是方法属性,值依赖于request._full_data
    

    4. 渲染模块(了解)

    浏览器和postman请求结果渲染的方式结果不一样!

    浏览器:

    Postman:

    # 源码入口: APIview dispatch里的 527行
    # 拿到视图函数 response 后 调用finalize_response 传入 response 做二次封装!
    self.response = self.finalize_response(request, response, *args, **kwargs)
    

    finalize_response

     def  finalize_response(self, request, response, *args, **kwargs):
            # 断言判断是否是HttpResponseBase的子类 必通过
            assert isinstance(response, HttpResponseBase), (....)
            
            # 判断实例response是否是drf response 的子类
            # 注: response是视图函数返回的结果
            # <Response status_code=200, "text/html; charset=utf-8">  
            if isinstance(response, Response):
                
                # 判断如果 反射出 允许的renderer没有值 就调用perform_content_negotiation
                if not getattr(request, 'accepted_renderer', None):
                    # 这里点击去
                    neg = self. **perform_content_negotiation** (request, force=True)
                    # 解压赋值
                    request.accepted_renderer, request.accepted_media_type = neg
    
                # 允许的renderer
                response.accepted_renderer = request.accepted_renderer
                response.accepted_media_type = request.accepted_media_type
                # 渲染器上下文 调用方法get_renderer_context 这里点击去
                response.renderer_context = self.**get_renderer_context()**
    
            # 最后返回response对象
            return response
    

    perform_content_negotiation

        def perform_content_negotiation(self, request, force=False):
            # 获取渲染器 这里点击去
            renderers = self.get_renderers()
            conneg = self.get_content_negotiator()
            ...
    

    get_renderers

        def get_renderers(self):
            # 列表生成式 循环 renderer_classes 
            # renderer_classes单例集合(得到的结果就有一个) renderer_classes 这里点击去
            return [renderer() for renderer in self.renderer_classes]
    

    renderer_classes

    class APIView(View):
        # 走得Api settings 里的 DEFAULT_RENDERER_CLASSES
        renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
        ...
    

    5. Drf配置(重点)

    drf APISettings 默认配置

    文件在 drf的settings.py 里的 APISttings

    DEFAULTS = {
        # Base API policies
        # Apisettings 里的配置
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',
        ],}
    

    drf框架 自定义 全局配置

    在自己的settings.py文件中 配置

    REST_FRAMEWORK = {
        # 全局配置解析类:适用于所有视图类
        'DEFAULT_PARSER_CLASSES': [
            # 'rest_framework.parsers.JSONParser',
            # 'rest_framework.parsers.FormParser',
            # 'rest_framework.parsers.MultiPartParser'
        ],
        # 全局配置渲染类:适用于所有视图类
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            # 'rest_framework.renderers.BrowsableAPIRenderer',  # 上线后尽量关闭
        ],
        # 异常模块:异常处理函数
        # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
        'EXCEPTION_HANDLER': 'api.exception.exception_handler',
    }
    

    drf框架 自定义 局部配置

    view.py文件中的视图类中配置

    class BookAPIView(APIView):
        # 局部配置解析类:只适用当前视图类
        parser_classes = [JSONParser, FormParser, MultiPartParser]
        # 局部配置渲染类:只适用当前视图类
        renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
    

    xxx_classes都在 APIview里面可以看到

    # renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    # parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    

    6.解析模块

     # 代码入口:
     # 二次封装了request 原来的是wsgi的 现在的是restframework
     # 包含解析模块
     request = self.initialize_request(request, *args, **kwargs)
    
       def initialize_request(self, request, *args, **kwargs):
            # 解析的内容 解析数据
            parser_context = self.get_parser_context(request)
    
            # 返回 request对象 类加括号实例化
            # Request 是 from rest_framework.request import Request
            return Request(
                request,
                parsers=self.get_parsers(), # 这里就是真正的配置解析了
                authenticators=self.get_authenticators(),
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
    
    

    get_parser_context

      def get_parser_context(self, http_request):
            # 直接返回了 一个字典 没有做解析 只是返回了 要解析的数据
            return {
                'view': self, # 请求的视图 封装到view里面
                'args': getattr(self, 'args', ()), # 映射 args 和 kwargs
                'kwargs': getattr(self, 'kwargs', {})
            }
    

    get_parsers

      def get_parsers(self):
            # 熟悉的 列表生成器
            return [parser() for parser in self.parser_classes]
    

    解析配置:

    全局

    REST_FRAMEWORK = {
        # 全局配置解析类:适用于所有视图类
        'DEFAULT_PARSER_CLASSES': [
            # 'rest_framework.parsers.JSONParser',
            # 'rest_framework.parsers.FormParser',
            # 'rest_framework.parsers.MultiPartParser'
        ]
    }
    

    局部

    class BookAPIView(APIView):
        # 局部配置解析类:只适用当前视图类
        parser_classes = [JSONParser, FormParser, MultiPartParser]
    

    自定义解析模块

    创建文件夹 utils 创建py文件 自定义解析类 继承parsers.py 下的 BaseParser 重写 parse

    # 这里的parse直接就抛异常了 也就是说 继承BaseParser 可以自定义解析
    class BaseParser:
        """
        All parsers should extend `BaseParser`, specifying a `media_type`
        attribute, and overriding the `.parse()` method.
        """
        media_type = None
        # 自定义解析 一定要重写parse方法
        def parse(self, stream, media_type=None, parser_context=None):
            """
            Given a stream to read from, return the parsed representation.
            Should return parsed data, or a `DataAndFiles` object consisting of the
            parsed data and files.
            """
            raise NotImplementedError(".parse() must be overridden.")
    

    7.异常模块

    入口: 监听 三大认证以及视图内的错误 逻辑错误都是错误

            try: 
                # 三大认证 (认证, 权限 , 评率) , 用来替换csrf安全认证, 要比csrf认证强大的多
                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
                # 最后加了括号传入参数 执行了 exception_handler
                response = handler(request, *args, **kwargs)
    		# 入口:
            except Exception as exc:
                # 异常模块 处理请求异常分支
                response = self.handle_exception(exc)
    

    handle_exception

    def handle_exception(self, exc):
        ....
        # 生成了 exception_handler 获取异常处理的句柄 (重点!!!!)
        exception_handler = self.get_exception_handler() 
    	# get_exception_handler_context 内部就放回了一个封装好了的 view request args 的字典
        context = self.get_exception_handler_context()
        # 最后将 外面捕获的到异常和 字典传入 exception_handler (重点)
        # 异常处理的结果
        response = exception_handler(exc, context)
        # 判断是否是空
    	if response is None:
            # 没有异常内容 抛出异常信息 (相当于没有detail全部是代码的异常内容)
        	self.raise_uncaught_exception(exc)
    	response.exception = True
        # 有异常内容返回内容
    	return response
    

    exception_handler = self.get_exception_handler()

        def get_exception_handler(self):
            # 放回了 settings里面配置的函数exception_handler
            return self.settings.EXCEPTION_HANDLER
    

    找到 settings 下的 EXCEPTION_HANDLER

    DEFAULTS = {
        'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    }
    # 导入的是rest_framework.views 下的 exception_handler
    

    找到view下的 exception_handler

    def exception_handler(exc, context):
    		.....
            # 一系类的判断 最后返回的data是 {'detail':'xxxx'}
            return Response(data, status=exc.status_code, headers=headers)
    	# 如果判断都不满足的话返回none 然后 交给 self.raise_uncaught_exception(exc) 处理(返回全部是代码)
        return None
    
    

    自定义异常处理(重点)

    为什么要自定义异常模块?

    """
    1)所有经过drf的APIView视图类产生的异常,都可以提供异常处理方案
    2)drf默认提供了异常处理方案(rest_framework.views.exception_handler),但是处理范围有限
    3)drf提供的处理方案两种,处理了返回异常现象,没处理返回None(后续就是服务器抛异常给前台)
    4)自定义异常的目的就是解决drf没有处理的异常,让前台得到合理的异常信息返回,后台记录异常具体信息
    """
    

    在app下创建py文件 写入方法:

    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework.response import Response
    # 自定义方法exception_handler(exc,content)
    def exception_handler(exc,content):
        # 什么也别干 数据交给 drf view的exception_handler处理
        response = drf_exception_handler(exc,content)
        data = {'detail':"%s %s %s"%(content['view'], content['request'].method, exc)}
        # 判断是否是空 是空说明 异常不满足drf_exception_handler内的判断
        if not response:  # 服务端错误
            response = Response({'detail': data})
        else:
            response.data = {'detail': data}
            # 核心:要将response.data.get('detail')信息记录到日志文件
            # logger.waring(response.data.get('detail'))
        return response
    

    settings全局配置:

    # 自定义drf配置
    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'api.exception.exception_handler',
    }
    

    8.响应模块

    响应类构造器:rest_framework.response.Response

    from rest_framework.response import Response
    

    Response下的__init__

    def __init__(self, data=None, status=None,
                     template_name=None, headers=None,
                     exception=False, content_type=None):
         """
            :param data: 响应数据
            :param status: http响应状态码
            :param template_name: drf也可以渲染页面,渲染的页面模板地址(不用了解)
            :param headers: 响应头
            :param exception: 是否异常了
            :param content_type: 响应的数据格式(一般不用处理,响应头中带了,且默认是json)
        """
        pass
    

    使用:常规实例化响应对象

    # status就是解释一堆 数字 网络状态码的模块
    from rest_framework import status就是解释一堆 数字 网络状态码的模块
    # 一般情况下只需要返回数据,status和headers都有默认值
    return Response(data={数据}, status=status.HTTP_200_OK, headers={设置的响应头})
    

    自定义response(重点)

    from rest_framework.response import Response
    class APIResponse(Response):
        def __init__(self, status=0, msg='ok', results=None, http_status=None,
                     headers=None, exception=False, content_type=None, **kwargs):
            # 将status、msg、results、kwargs格式化成data
            data = {
                'status': status,
                'msg': msg,
            }
            # results只要不为空都是数据:False、0、'' 都是数据 => 条件不能写if results
            if results is not None:
                data['results'] = results
            # 将kwargs中额外的k-v数据添加到data中
            data.update(**kwargs)
    
            super().__init__(data=data, status=http_status, headers=headers, exception=exception, content_type=content_type)
    
  • 相关阅读:
    模态对话框可能导致程序崩溃
    C++实现将字符串循环左移n个位置
    Android Gallery图片显示和文字提示及Menu 菜单
    android代码实现ScaleAnimation
    Android系统内存管理的问题
    android打电话发短信
    android开发之屏幕设置
    输入法
    黄金周
    气筒
  • 原文地址:https://www.cnblogs.com/lddragon1/p/12111757.html
Copyright © 2011-2022 走看看