安装
pip install djangorestframework
urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^dog/', views.DogView.as_view())
]
views.py
from rest_framework.views import APIView
class DogView(APIView):
def get(self, request, *args, **kwargs):
# 这里的 request 是rest framework加工之后的 request,不再是原来的request了
print(request)
ret = {
'code': 1000,
'msg': 'xxx'
}
return HttpResponse(json.dumps(ret), status=201)
def post(self, request, *args, **kwargs):
return HttpResponse('创建Dog')
def put(self, request, *args, **kwargs):
return HttpResponse('更新Dog')
def delete(self, request, *args, **kwargs):
return HttpResponse('删除Dog')
源码流程
封装 request
当请求进来,执行 dispatch
,自己没有找父类 APIView
的 dispatch
def dispatch(self, request, *args, **kwargs):
... # 省略的内容
self.kwargs = kwargs
# 对原生的request进行加工(丰富了一些功能)
'''
# 鼠标点进 initialize_request,返回一个对象
return Request(
request, # 这里才是原生的request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
'''
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
...
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# 返回Request类的一个对象,交给上面的request
return Request(
request, # 这里才是原生的request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # 这里有个 get_authenticators
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
问题:如果执行 self.get_authenticators()
,流程是怎样的,哪里是入口?
'''
应该从 DogView 做入口,因为里面所有传过去的self,都是 DogView 的对象
DogView 没有再往父类找
这里 DogView 没有 get_authenticators(),并且这个方法应该加了s,猜测应该是个复数
鼠标点进去,查看父类
'''
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
'''
可以看到,返回了一个列表,如果 self.authentication_classes 也是一个列表,并且
self.authentication_classes = [Foo, Bar]
列表里面是两个类,则
[auth() for auth in self.authentication_classes] 就是对每个类进行实例化
所以谁调用这个 get_authenticators 方法,返回的就是 [Foo, Bar] 的对象
即 [Foo(), Bar()]
当然这些目前只是猜测
'''
根据以上猜测,来查看一下
'''
这里执行的是 self.authentication_classes,DogView 没有,去父类查看
'''
class APIView(View):
... # 其他的内容
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
...
'''
这其实是读取了 rest_framework的配置文件,是在 APIView中写的
找的时候优先到 DogView 中查找,没有再去父类
'''
现在为 DogView
加上 authentication_classes
,用的就是 DogView
的方法
from rest_framework.views import APIView
from rest_framework.authentication import BasicAuthentication
class DogView(APIView):
authentication_classes = [BasicAuthentication, ] # 增加的
def get(self, request, *args, **kwargs):
self.dispatch()
ret = {
'code': 1000,
'msg': 'xxx'
}
return HttpResponse(json.dumps(ret), status=201)
def post(self, request, *args, **kwargs):
return HttpResponse('创建Dog')
def put(self, request, *args, **kwargs):
return HttpResponse('更新Dog')
def delete(self, request, *args, **kwargs):
return HttpResponse('删除Dog')
现在增加了 authentication_classes
,回到上面的 get_authenticators
方法,它返回的是 [BasicAuthentication(), ]
对象,再将它交给 Request
对象
所以新的 Request
对象目前封装了两个值,一个是原生的 request
,一个是 [BasicAuthentication(),]
对象列表
回到 dispatch
def dispatch(self, request, *args, **kwargs):
... # 省略的内容
self.kwargs = kwargs
# 对原生的request进行加工(丰富了一些功能)
'''
# 鼠标点进 initialize_request,返回一个对象
return Request(
request, # 这里才是原生的request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
这里就是
return Request(request, [BasicAuthentication(), ])
'''
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
... # 省略的内容
现在是封装进去了,那么要取出,该怎么取呢?(可以通过 request
点出某个属性或方法)可以查看一下 Request
class Request(object):
... # 省略的内容
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 # 如果要拿原生的request,需要执行 request._request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
... # 省略的内容
# 再回到 dispatch()
def dispatch(self, request, *args, **kwargs):
... # 省略的内容
self.kwargs = kwargs
# 对原生的request进行加工(丰富了一些功能)
'''
# 鼠标点进 initialize_request,返回一个对象
return Request(
request, # 这里才是原生的request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
这里就是
return Request(request, [BasicAuthentication(), ])
'''
request = self.initialize_request(request, *args, **kwargs)
# 获取原生的request: request._request
# 获取认证类的对象: request.authenticators
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# 将上面的 request 放进来,执行initial,DogView没有,去父类查找
self.initial(request, *args, **kwargs)
... # 省略的内容
认证
# 来到 initial
# 这里的 request 是封装后的 request
def initial(self, request, *args, **kwargs):
... # 省略的内容
# 这里的request是带有原生的request和认证的对象的request
# 执行 perform_authentication,DogView 没有,去父类查找
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
# 来到 perform_authentication
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
request.user
# 通过Request实例化后的对象找到 .user
@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() # 注意这里,去 _authenticate
return self._user
# 来到 _authenticate
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
# 这里的 self.authenticators 就是加工后的request中的第二个值,
# 也就是 [BasicAuthentication对象, ]
# 这一步就是循环认证类的所有对象
for authenticator in self.authenticators:
try:
# 拿到上面的[BasicAuthentication对象, ],里面有个 .authenticate 方法
# 如果这个方法抛出异常,就会被下面的except捕获到
# 如果没有异常,则有返回值(返回值有两种,一个是None,一个是有确切的值)
# 有确切的返回值,必须是元组:(request.user, request.auth)
# 如果是None,表示本次认证对象不做处理,继续做下一次的循环处理
# 如果所有对象都为None,那么 request.user, request.auth均未赋值
# 等循环玩走最后一步的 self._not_authenticated()
# 括号里的 self 是 request 对象,当前代码是在 request.py 中
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() # 所有对象都为None,走这一步
def _not_authenticated(self):
"""
Set authenticator, user & authtoken representing an unauthenticated request.
# 如果所有对象都为None,这里默认设置一个匿名用户
Defaults are None, AnonymousUser & None.
"""
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() # AnonymousUser
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() # None
else:
self.auth = None
以上便是需要掌握的源码流程,基于上面的思路,可以做一个登录认证的示例
总结
1、使用
- 创建类,继承
BaseAuthentication
,实现authenticate
方法 - authenticate 方法有三种返回值
- None,表示让下一个认证来执行
- 抛出
AuthenticationFailed
异常,表示认证失败 - 元组形式,
(元素1, 元素2)
,元素1
赋值给request.user
,元素2
赋值给request.auth
- 局部使用,在类中写静态字段,
authentication_classes = [Authentication, ]
- 全局使用,在配置文件中添加
REST_FRAMEWORK
2、源码流程
先走 dispatch()
,对 request
进行封装,然后执行 initial
,在 initial
中进行认证。执行认证的流程是去找 request.user
,在 request.user
中找之前封装的 request
的所有认证对象,再循环所有的认证对象,执行 authenticate
方法
- dispatch
- 封装 request
- 获取定义的认证类(全局 / 局部),通过列表生成式创建对象
- initial
- perform_authentication
- request.user(内部循环 ...)
- perform_authentication
- 封装 request