DRF ---- APIview生命周期 请求/渲染/解析/异常/响应/ 模块源码 drf配置

2021-01-21 07:13

阅读:722

标签:defaults   来替   ORC   ati   出现   json   efault   class   present   

目录

  • drf框架的封装风格
  • 1. 原生Django View的源码复习
    • as_view源码
    • dispatch源码
  • 2. ApiView的生命周期(源码)
    • 重写的as_view源码
    • 重写的dispatch源码
  • 3 . 请求模块
    • initialize_request 源码
    • Request 源码
    • Request 下 __getattr_ 源码
    • 总结(重点)
  • 4. 渲染模块(了解)
  • 5. Drf配置(重点)
    • drf APISettings 默认配置
    • drf框架 自定义 全局配置
    • drf框架 自定义 局部配置
  • 6.解析模块
    • 解析配置:
      • 全局
      • 局部
    • 自定义解析模块
  • 7.异常模块
    • 自定义异常处理(重点)
  • 8.响应模块
    • 使用:常规实例化响应对象

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)\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\d+)', views.DrfCBV.as_view())

重写的as_view源码

1 ApiView继承View类 重写了as_viewdispatch方法

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是视图函数返回的结果
        #   
        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={设置的响应头})

DRF ---- APIview生命周期 请求/渲染/解析/异常/响应/ 模块源码 drf配置

标签:defaults   来替   ORC   ati   出现   json   efault   class   present   

原文地址:https://www.cnblogs.com/lddragon1/p/12111757.html


评论


亲,登录后才可以留言!