内容简介:忘羡一曲远,曲终人不散我在前面的博客里已经讲过了,我们一般编写API的时候用的方式这种方式的有点是,灵活性比较大,可以根据自己的业务逻辑来自定义各种组件逻辑。
忘羡一曲远,曲终人不散
我在前面的博客里已经讲过了,我们一般编写API的时候用的方式
class CoursesView(ViewSetMixin,APIView):
pass
这种方式的有点是,灵活性比较大,可以根据自己的业务逻辑来自定义各种组件逻辑。
这边文章,我将带大家对 认证组件做一个源码分析,写这边文章的原因也是因为自己虽然知道怎么写,但是不知道为什么要这么写,今天看了源码,所以想把自己的一些心得写出来和大家分享。
先看代码
视图类
class ShoppingCarView(ViewSetMixin, APIView):
authentication_classes = [LuffyAuthtication,]
def list(self,request, *args, **kwargs):
"""
查看购物车信息
:param args:
:param kwargs:
:return:
"""
try:
ret = BaseResponse()
pay_course_list = []
# key = 'shoppingcar_%s_%s' % (USERID, '*')
key = settings.SHOPCAR_FORMAT.format( request.user.id, "*")
user_key_list = COON.keys(pattern=key) # 取到这个用户对应的所有课程字典 对应的键
for key in user_key_list:
# 对应的每个键值 去取每个课程对应的信息 和价格列表
temp = {
'id': COON.hget(key, 'id').decode('utf8'),
'name': COON.hget(key, 'name').decode('utf8'),
'img': COON.hget(key, 'img').decode('utf8'),
'default': COON.hget(key, 'default').decode('utf8'),
'price_dict': json.loads(COON.hget(key, 'price_dict').decode('utf8')),
}
pay_course_list.append(temp)
ret.data = pay_course_list
except Exception as e:
ret.data = '查看失败'
ret.code = 00000
return Response(ret.dict)
认证类
from app01 import models
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class LuffyAuthtication(BaseAuthentication):
def authenticate(self, request):
"""
用于做用户认证
:param request:
:return:
"""
token = request.query_params.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise AuthenticationFailed({'code':10000,'error':'认证失败'})
return token_obj.user,token_obj
url.py
urlpatterns = [
url(r'^payment/$', payment.PaymentView.as_view({'post': 'create','put': 'update','get':'list'})),
]
以上的就是逻辑代码,就是一个必须要验证为注册用户才能查看购物车的内容。
下面来进行源码分析,来看代码是如何走到我自定义的认证类中的。
1.首先从url中分析
1.先来到视图类中的as.view()方法
而我们的自定义的方法中没有as.view()方法,那就要去父类ViewSetMixin和APIView中去找,好看源码
2.分析源码
1.先看ViewSetMixin类中
class ViewSetMixin(object):
"""
This is the magic.
Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource.
For example, to create a concrete view binding the 'GET' and 'POST' methods
to the 'list' and 'create' actions...
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
"""
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
# The suffix initkwarg is reserved for displaying the viewset type.
# eg. 'List' or 'Instance'.
cls.suffix = None
# The detail initkwarg is reserved for introspecting the viewset type.
cls.detail = None
# Setting a basename allows a view to reverse its action urls. This
# value is provided by the router through the initkwargs.
cls.basename = None
# actions must not be empty
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
# sanitize keyword arguments
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" % (
cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions
# Bind methods to actions
# This is the bit that's different to a standard view
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# And continue as usual
# 前面都是在对传参做判断和重新赋值,重要的是下面这一步,最后return 调用了dispatch方法
return self.dispatch(request, *args, **kwargs)
2.找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)
## request = Request(.....)
self.request = request
self.headers = self.default_response_headers
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
所有的关键点都在dispatch方法里面:
(1) request = self.initialize_request(request, *args, **kwargs)
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(), #[BasicAuthentication(),],把对象封装到request里面了
negotiator=self.get_content_negotiator(), parser_context=parser_context )
(2)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]
(3)authentication_classes
APIView里面有个 authentication_classes 字段 ,这里如果写了静态变量就直接在类的静态变量中找,否则就到全局配置文文件中去找 可以看到默认是去全局的配置文件找(api_settings)
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
(4)开始验证
(5)self.initial(request,*args,**kwargs)
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) 频率
(6)self.perform_authentication(request)
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,那这个user方法是谁的呢????
还记得这个Request吧,对没错就是他的,我们点进去看看
(7)request.user (8)self._authenticate()方法
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
#循环认证类的所有对象
for authenticator in self.authenticators:
try:
#执行认证类的authenticate方法
#这里分三种情况
#1.如果authenticate方法抛出异常,self._not_authenticated()执行
#2.有返回值,必须是元组:(request.user,request.auth)
#3.返回None,表示当前认证不处理,等下一个认证来处理
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()
返回的元组就是上面自定义认证类代码中的
token_obj.user, 可以再后续的request.user中取到
token_obj ,可以再后续的request.auth中取到
到这里整个认证组件的源码就分析完了
自己写认证类方法梳理
1.创建认证类
继承BaseAuthentication
- 1.重写authenticate方法;
- 2.authenticate_header方法直接写pass就可以(这个方法必须写)
2.authenticate()返回值(三种)
- None —–»>当前认证不管,等下一个认证来执行,可用于对请求的过滤上
- raise exceptions.AuthenticationFailed(‘用户认证失败’)
from rest_framework import exceptions - 有返回值元祖形式:(元素1,元素2) #元素1复制给request.user; 元素2复制给request.auth
3.局部使用
-
authentication_classes = [BaseAuthentication,]
4.全局使用
#设置全局认证 REST_FRAMEWORK = { “DEFAULT_AUTHENTICATION_CLASSES”:[‘API.utils.auth.Authentication’,] }
源码流程
-
—»dispatch
-
—>封装request
-
—>获取定义的认证类(全局/局部),通过列表生成式创建对象
-
—>initial
-
—>peform_authentication
- —>request.user (每部循环创建的对象)
-
-
-
-
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Algorithms on Strings, Trees and Sequences
Dan Gusfield / Cambridge University Press / 1997-5-28 / USD 99.99
String algorithms are a traditional area of study in computer science. In recent years their importance has grown dramatically with the huge increase of electronically stored text and of molecular seq......一起来看看 《Algorithms on Strings, Trees and Sequences》 这本书的介绍吧!
RGB转16进制工具
RGB HEX 互转工具
SHA 加密
SHA 加密工具