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
方法, 主体还是View
的as_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)