DRF认证源码分析图
1. CBV的实现原理
通过as_view方法,执行dispatch函数,在dispatch函数中通过request的method方法,调用视图类的不同函数
as_view方法, 执行dispatch方法
@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) ::# 执行dispatch方法::
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
dispatch方法
判断request请求的method方法,根据method反射,执行类视图中对应的函数
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names: # 判断request请求的method方法
handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 根method执行反射
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
2. 继承rest framework 的APIView类
APIView继承了Django的View,并且重写了dispatch方法
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
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs) # 执行initial方法
# 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
重写的dispatch方法除了保留分发的功能,同时做了以下几个操作:
- 封装request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
- 执行initial方法
self.initial(request, *args, **kwargs) # 注意此时的request是重新封装后的request
2.1 封装request
封装的request返回一个Request类的实例对象
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request, # 传入原生的request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # 传入执行get_authenticators()方法后的返回值
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
2.1.1 get_authenticators方法
authentication_classes是一个放置认证类的列表,那么get_authenticators方法返回的就是认证类的实例化对象列表,传入到重写的request对象中。
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是一个列表
2.2 执行initial方法
perform_authentication函数
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) # 执行perform_authentication函数
self.check_permissions(request)
self.check_throttles(request)
2.2.1 perform_authentication函数
调用封装后的requestd对象的user
def perform_authentication(self, request):
"""
Perform authentication on the incoming request.
Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
request.user
2.2.2 request.user
user方法调用了_authenticate方法
@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
2.2.2.1 _authenticate方法
self.authenticators其中self指的是封装后的request对象
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) #循环执行认证类中的authenticate方法
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()
看Request类,我们发现self.authenticators指的是重新封装request对象时,传入的认证类对象列表[auth( ) for auth in self.authentication_classes]
那么上面的_authenticate方法就是对认证类对象列表进行循环执行,并且是执行认证类对象中的authenticate方法,将返回值user_auth_tuple复制给self.user和self.auth
class Request(object):
"""
Wrapper allowing to enhance a standard `HttpRequest` instance.
Kwargs:
- request(HttpRequest). The original request instance.
- parsers_classes(list/tuple). The parsers to use for parsing the
request content.
- authentication_classes(list/tuple). The authentications used to try
authenticating the request's user.
"""
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or () # self.authenticators
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
本文是根据老男孩IT教育的wupeiqi老师的Django rest framework源码分析视频理解整理而成