rest_framework的APIView源码解析
APIView概述
这个类继承了View类可以继承他来实现CBV模式的路由映射。他自身有增加了很多方法。这篇博客主要讲解APIView中的认证功能,权限组件,频率组件,as_view,dispatch方法

APIView的as_view方法
当我们在url(r'/', xxx.as_view())时 as_view方法做了什么?
在url.py中我们只要对一个我们继承了APIView的类使用as_view方法就可以实现和FBV一样的功能,那么as_view方法究竟做了什么呢?
def as_view(cls, **initkwargs): """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation view = super(APIView, cls).as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view)
在as_view函数中返回的是一个不同通过csrf_token验证的view,而这个view是执行View类中的as_view方法得到的,我们再去看看View类中的as_view方法做了什么事。
@classonlymethod def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return view
在View中as_view中定义了一个内部函数view,转而直接把view这个函数名返回了,这不是和我们在views.py中定义一个函数然后把函数名写在url()中本质上不就是一样的吗。所以现在我们知道了每次访问进来,就执行内部函数view方法。
当请求进来时,view方法执行又做了什么?
当请求进来时执行view方法
def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs
view中执行了dispatch方法,因为在View中和APIView中都定义了dispatch方法,而APIView是继承的View所以APIView的dispatch方法覆盖了View的dispatch方法。执行的是APIView的方法
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method 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 response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
每次请求进来用请求的方式去我自定义的类表中的方法匹配是否在我的列表中,如果在就用反射在找出和请求方法相同名称的函数然后执行并返回他。
在这个方法中我们还需要关注这么几个点,可以看到request被再一次封装了,然后执行了对request执行了initial方法。后面讲组件的时候会再次提到他。
写着写着突然发现认证组件,
认证组件
在对request进行initial方法时,执行了如下三个方法
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
# 认证系统
self.perform_authentication(request)
# 权限系统
self.check_permissions(request)
# 访问频次
self.check_throttles(request)
perform_authentication中 request.user我们可以猜想request.user是一个类方法,request被initialize_request封装了,
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context )
其中是由Request分装了,在Resquest中我们看看是否有这个函数
@property def user(self): """ Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """ if not hasattr(self, '_user'): with wrap_attributeerrors(): self._authenticate() return self._user
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ for authenticator in self.authenticators: try: user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated()
这里有一个重点我们必须知道代码中self.authenticators是什么为什么可以循环他。我们在看一下initial_request中的代码authenticators 是由self.get_authenticators()得到的。
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
接着我们可以找到authentication_classes是在APIView中api_settings中赋值的是一个默认的设置值,APISettings重写了__getattr__方法,取属性是先去找我们的settings中有没有如果没有就是用默认的认证系统。因为是在APIView中定义的那么我们在最外层的子类可以直接定义我们所需要的权限类。
好了现在我们知道self.authenticators是什么了,是一个个认证类的示例,而且看request.user我们可以发现这个认证类中一定要定义个authenticate方法。可以看到authenticate应该返回的是一个user和auth信息返回的是一个元祖。
权限组件和频率组件的源码和认证组件的源码如出一辙就不细细分析了。
如果三步验证都成功那么会正常返回值,不然就会终端请求抛错。

浙公网安备 33010602011771号