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

Posted LD_Dragon_Sky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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)

以上是关于DRF ---- APIview生命周期 请求/渲染/解析/异常/响应/ 模块源码 drf配置的主要内容,如果未能解决你的问题,请参考以下文章

drf模块及源码

drf 整体流程

APIView请求生命周期

DRF学习

DRF框架

DRF框架知识总览