标签:min format exception 过程 **kwargs asa anti 重写 attribute
纸上得来终觉浅,绝知此事要躬行。
在之前的DRF源码分析对比原生Django介绍了二者的区别,最后分析得出DRF
对原生的dispatch
方法做了很多改进,本章就接着分析APIView
下的dispatch
到底做了那些事?
通过上次的分析主要分为以下几个模块:
rest_framework/views.py
下的APIView
类中的dispatch
方法:request = self.initialize_request(request, *args, **kwargs)
,ctrl+b
进入: def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# 返回Request类的对象
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
Request
类的源码,查看实例化的过程简化版:class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
......
# 二次封装request,将原生request作为drf Request 对象的 _request属性
self._request = request
......
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
@property
def data(self):
if not _hasattr(self, ‘_full_data‘):
self._load_data_and_files()
return self._full_data
def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
分析:
例如获取请求参数:
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
通过源码和分析,我们了解到在dispatch
中走到request = self.initialize_request(request, *args, **kwargs)
调用initialize_request
方法返回的是rest_framework/request.py
下的Request
类的对象,这个类在实例化的过程中对原生参数request
做了进一步封装和兼容,最终在返回到dispath
中的request
,整个请求模块彻底走完。
dispatch
运行到响应模块,代码如下:关键代码实现以及变量定义如下:
http_method_names = [‘get‘, ‘post‘, ‘put‘, ‘patch‘, ‘delete‘, ‘head‘, ‘options‘, ‘trace‘]
def http_method_not_allowed(self, request, *args, **kwargs):
"""
If `request.method` does not correspond to a handler method,
determine what kind of exception to raise.
"""
raise exceptions.MethodNotAllowed(request.method)
通过判断类视图是否定义过http_method_names
中的方法,不存设置handler=http_method_not_allowed
,而http_method_not_allowed
内部是直接抛出异常。
getattr
反射机制,handler
就是我们类视图定义的方法,执行response = handler(request, *args, **kwargs)
,返回response
,原生的Django也是这样做的。self.handle_exception(exc)
,点击参看源码,我做了一定的简化: def handle_exception(self, exc):
......
# 获取处理异常的方法
exception_handler = self.get_exception_handler()
context = self.get_exception_handler_context()
# 异常处理的结果
response = exception_handler(exc, context)
if response is None:
# 没有异常内容,抛出异常信息
self.raise_uncaught_exception(exc)
# 有异常内容,返回异常内容
response.exception = True
return response
def get_exception_handler(self):
"""
Returns the exception handler that this view uses.
"""
return self.settings.EXCEPTION_HANDLER
self.settings.EXCEPTION_HANDLER
,点击进入源码,我们可以发现:settings = api_settings
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
‘EXCEPTION_HANDLER‘: ‘rest_framework.views.exception_handler‘,
所以真正的异常处理是在rest_framework.views.exception_handler
这个函数
exception_handler
返回的结果进行处理:if response is None:
# 没有异常内容,抛出异常信息
self.raise_uncaught_exception(exc)
# 有异常内容,返回异常内容
response.exception = True
return response
exception_handler
函数,并且在settings.py
文件中指定exception_handler
函数的位置。例如:
# 自定义drf配置
REST_FRAMEWORK = {
# 全局配置异常模块
‘EXCEPTION_HANDLER‘: ‘api.exception.exception_handler‘,
}
========================下面就是导入原始的函数,然后先实现其原有的逻辑,之后在处理自己的逻辑====================
from rest_framework.views import exception_handler as drf_exception_handler # 防止与自定义的重名,起一个别名
def exception_handler(exc, context): # 注意名称必须为`exception_handler`
response = drf_exception_handler(exc, context)
if response is None:
view = context[‘view‘]
if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
# logger.error(‘[%s] %s‘ % (view, exc))
response = Response({‘detail‘: ‘服务器内部错误‘}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
return response
dispatch
的最后一部分,源码如下:def dispatch(self, request, *args, **kwargs):
......
# 渲染模块
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
finalize_response
源码查看,进行一定的简化: def finalize_response(self, request, response, *args, **kwargs):
"""
Returns the final response object.
"""
if isinstance(response, Response):
if not getattr(request, ‘accepted_renderer‘, None):
neg = self.perform_content_negotiation(request, force=True) # 获取解析类的对象
request.accepted_renderer, request.accepted_media_type = neg # 拆包
......
perform_content_negotiation
: def perform_content_negotiation(self, request, force=False):
"""
Determine which renderer and media type to use render the response.
"""
renderers = self.get_renderers() # 获得解析类对象
conneg = self.get_content_negotiator()
try:
return conneg.select_renderer(request, renderers, self.format_kwarg)
except Exception:
if force:
return (renderers[0], renderers[0].media_type)
raise
get_renderers
查看: def get_renderers(self):
"""
Instantiates and returns the list of renderers that this view can use.
"""
return [renderer() for renderer in self.renderer_classes]
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
api_settings中
获取渲染模块的配置:‘DEFAULT_RENDERER_CLASSES‘: [
‘rest_framework.renderers.JSONRenderer‘, # 只显示出json数据
‘rest_framework.renderers.BrowsableAPIRenderer‘, # 渲染出页面
],
至于渲染类我们就不做过多的分析,分析到这我们发现渲染类和异常类很相似,同样我们可以在settings.py
中配置,例如:项目上线之后我们只希望返回json
格式的数据,因此可以定义如下配置:
# 自定义drf配置
REST_FRAMEWORK = {
# 全局渲染类配置
‘DEFAULT_RENDERER_CLASSES‘: [
‘rest_framework.renderers.JSONRenderer‘,
# ‘rest_framework.renderers.BrowsableAPIRenderer‘,
]
}
当然我们在settings.py
中配置都是全局配置,也可以在CBV
的类视图中定义,因为我们继承了APIView
,下面我们可以查看一下源码的配置:
class APIView(View):
# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES # 渲染
parser_classes = api_settings.DEFAULT_PARSER_CLASSES # 解析
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 认证
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES # 限流
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES # 权限
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
这些配置项都在 rest_framework/settings.py
下,有兴趣可以去查看,而且DRF
的封装也比较规范,可以导入特定的类:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
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 #频率
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer # 渲染
所以我们在类视图可以如下定义:
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class User(APIView):
# 局部渲染配置
renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
def get():
pass
......
OK,看到这里,差不多整个dispatch
的代码执行流程全部走完了,除了一个三大认证以外,我打算用另一篇博文分析,休息一下继续肝。
标签:min format exception 过程 **kwargs asa anti 重写 attribute
原文地址:https://www.cnblogs.com/ydongy/p/13125065.html