首页 > 编程语言 >drf day08 三大认证源码分析、断点调试、全局异常处理

drf day08 三大认证源码分析、断点调试、全局异常处理

时间:2023-02-08 22:14:57浏览次数:40  
标签:None day08 return get self permission request 源码 断点

一、后端取COOKIE的几种方式

1.GET请求,数据携带在地址中
	从地址栏中取
	request.query_params.get('token')
2.原生djagno,取出前端传入cookie,从哪取的?
	request.COOKIE.get('sessionid')
3.后期如果想从请求头中取
request.META.get('HTTP_TOKEN')

二、三大认证源码分析

1.权限的源码执行流程

1.因为前面学过,三大认证在APIVIEW的dispatch中就要认证完成,所以我们去看dispatch
	497行左右, self.initial(request, *args, **kwargs)---》执行3大认证
    
2.initial没见过,点去看一下
 def initial(self, request, *args, **kwargs):

        self.format_kwarg = self.get_format_suffix(**kwargs)
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
	# 上面就是三大组件的执行
3.check_permissions(request)就是权限的,点进去看看
    def check_permissions(self, request):
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )
     # self.get_permissions()   self 就是视图类APIVIEW的对象
4.get_permissions点进来看下
    def get_permissions(self):

        return [permission() for permission in self.permission_classes]
 # 一个列表生成式,循环我们给视图层类配的那个权限的列表,然后他加 括号实例化了权限类的对象返回

5.又看回第三步,又是一个for循环,循环取权限对象调用has_permission()方法
 注意,对象来调类中的方法,会把该对象当做第一个参数传进去,所以其实has_permission(request, self)和我们自己写的has_permission(self, request, view)对应上了,没有少传参
    
6.has_permission()方法执行,返回对或者错,然后if not取反,意思就是如果返回false就走下面的(就是取反2333)
            self.permission_denied(
                request,
                message=getattr(permission, 'message', None),
                code=getattr(permission, 'code', None)
            )
    
    # 反射出了我们在has_permission()里面写的self.message,属性所对应的值
    
    
7.如果配置多个权限组件,只要有一个没过,后面的都不执行


总结:
	就是按照权限组件列表,一个个反射has_permission,如果有一个返回false,就结束了,意思就是权限认证不过
    还是局部配置最大,然后项目的配置次之,最后是drf内置的

2.认证的源码执行流程

1、2前两步还是一样的
3.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是基于Request类产生的权限的request,所以去找Request里面!找到了user属性
4.Request里面的,果然是被装饰器修饰的,方法变数据
@property
    def user(self):

        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user
    
    # 找不到_user,属性,所以就是执行了self._authenticate()
5.进入_authenticate()看看
    def _authenticate(self):
        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()
        
6.for authenticator in self.authenticators 似曾相识,点进去看看
    # 但是发现Request里面实在是没有authenticators属性,点了还在原地,想起来了应该是在APIVIEW类中,把老request搞成新request时弄的
    # 进入APIVIEW 的dispatch,找到
    request = self.initialize_request(request, *args, **kwargs)
    
    点击去initialize_request
    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )
7.点进去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]
    # 列表生成式
    
8回到第5步
user_auth_tuple = authenticator.authenticate(self)
抛异常就不说了
被返回了两个值,一个元组,我们自己写的认证类,返回的不是None,解压赋值后for循环结束
self.user, self.auth = user_auth_tuple
这个self.user就是我们自己写的认证类中返回的user,从此以后,后续的request.user就是登录用户

总结:
	1 配置在视图类上的认证类,会在执行视图类方法之前执行,在权限认证之前执行(位置)
    2 自己写的认证类,可以返回两个值或None
    3 后续可以从request.user 取出当前登录用户(前提是你要在认证类中返回)

3.频率的源码执行流程

1、2前两步还是一样的
3.进入check_throttles查看
def check_throttles(self, request):

    throttle_durations = []
    for throttle in self.get_throttles():
        if not throttle.allow_request(request, self):
            throttle_durations.append(throttle.wait())

            if throttle_durations:
                # Filter out `None` values which may happen in case of config / rate
                # changes, see #1438
                durations = [
                    duration for duration in throttle_durations
                    if duration is not None
                ]

                duration = max(durations, default=None)
                self.throttled(request, duration)
                
4. 我们自己写的频率类没写allow_request(request, self) 这个方法,但是我们继承了SimpleRateThrottle,这个类给我们重写了allow_request方法      
5.allow_request的源码就不读了,因为大致知道了,如果超频了,就返回False,如果没超频,返回True

4.自定义频率类

​ 继承BaseThrottle,自己重写def allow_request(self, request, view):这个方法

先不重写具体代码,就返回对,这时候频率加了和没加一样的,返回错就一直被限制
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle


class MyThrottling(BaseThrottle):
    scope = 'xixi'

    def allow_request(self, request, view):
        return 1 # 或者False
    
————————————————————————————————————————————————————————

# (1)取出访问者ip
# (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走 {ip地址:[时间1,时间2,时间3,时间4]}
# (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
# (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
# (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败

class MyThrottling(BaseThrottle):
    scope = 'xixi'  # 这个别看,以前写的
    all_dict = {}

    def allow_request(self, request, view):
        visit_ip = request.META.get('REMOTE_ADDR')
        time_now = time.time()
        if visit_ip not in self.all_dict:
            self.all_dict[visit_ip] = [time_now, ]
            return 1
        while self.all_dict.get(visit_ip) and time_now - self.all_dict.get(visit_ip)[-1] > 60:
            self.all_dict.get(visit_ip).pop()

        if len(self.all_dict.get(visit_ip)) < 3:

            self.all_dict.get(visit_ip).append(time_now)
            print(self.all_dict)
            return 1
        else:
            return False

    

5.SimpleRateThrottle源码分析(不是很难)

# 写一个频率类,重写allow_request方法,在里面实现频率控制

# SimpleRateThrottle---》allow_request
    def allow_request(self, request, view):
		# 这里就是通过配置文件和scop取出 频率限制是多少,比如一分钟访问5此
        if self.rate is None:
            return True

        # 返回了ip,就以ip做限制
        self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True
		# 下面的逻辑,跟咱们写的一样
        self.history = self.cache.get(self.key, [])
        self.now = self.timer()
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()
        if len(self.history) >= self.num_requests:
            return self.throttle_failure()
        return self.throttle_success()
    
    
    
# SimpleRateThrottle的init方法
    def __init__(self):
        if not getattr(self, 'rate', None):
            # self.rate= '5、h'
            self.rate = self.get_rate()
        # 5    36000
        self.num_requests, self.duration = self.parse_rate(self.rate)
# SimpleRateThrottle的get_rate() 方法
    def get_rate(self):

        if not getattr(self, 'scope', None):
            msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                   self.__class__.__name__)
            raise ImproperlyConfigured(msg)

        try:
            #  self.scope 是 lqz 字符串
            # return '5/h'
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)
            
            
#     SimpleRateThrottle的parse_rate 方法
	def parse_rate(self, rate):
        # '5/h'
        if rate is None:
            return (None, None)
        # num =5
        # period= 'hour'
        num, period = rate.split('/')
        # num_requests=5
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        # (5,36000)
        return (num_requests, duration)

三、基于APIVIEW编写分页

分页还是只给查询所有接口‘

class BookView(ViewSetMixin, APIView):
    permission_classes = []
    authentication_classes = []

    def list(self, request):
        books = Book.objects.all()
        # 使用步骤
        # 1 实例化得到一个分页类的对象
        paginator = CommonLimitOffsetPagination()
        # 2 调用分页类对象的paginate_queryset方法来完成分页,返回的page是 要序列化的数据,分页好的
        page = paginator.paginate_queryset(books, request, self)
        if page is not None:
            serializer = BookSerializers(instance=page, many=True)
            # 3 返回数据,调用paginator的get_paginated_response方法
            # return paginator.get_paginated_response(serializer.data)
            return Response({
                'count': paginator.count,
                'next': paginator.get_next_link(),
                'previous': paginator.get_previous_link(),
                'results': serializer.data
            })

四、断点调试(重要)

说明

	断点调试是程序编写过程中一重要步骤,对于简单的程序可以使用print语句输出对应的结果,可以比较快速的分析出程序出现的问题在哪里,但是程序比较复杂时,如函数和变量比较多的情况,输出相应的变量值也难以找到程序错误的地方,这个时使用断点调试就能够跟踪程序的运行过程,结合运行过程中相应的变量变化能够比较快地判断出程序大概出现问题的地方,所以学会断点调试是非常重要的。

使用举例

​ 1.下断点标记符号

​ 在代码栏的左边可以点一个红灯小标出来,即可下断点

​ 2.执行DEBUG模式

​ 点击甲壳虫图标,就进入DEBUG,程序会卡在断点位置

​ 3.主栏显示当前断点处变量此刻的值,复杂代码下也能知道此刻都传了啥过来,里面有啥

STEP OVER

​ 执行当前的函数或者语句,不会进入当前函数的具体方法,执行完当前的语句之后直接跳到下一句。

STEP INTO

​ 如果某行调用其他模块的函数,可以进入函数内部,会跳到调用函数的地方执行。

五、全局异常处理

1.首先异常的情况

​ 一、自己主动抛

​ 二、真的代码有问题,程序出错

2.drf对于自己异常和其他异常的区别对待

drf对于自己内置的,比如
from rest_framework.exceptions import APIException,AuthenticationFailed
这种,是可以抛给前端一个很标准的格式,并且程序不崩

raise APIException('啊??')
>>>
前端:
{
    "detail": "啊??"
}
————————————————————————————————————————
其他的就会崩掉程序
from rest_framework.views import exception_handler
这是drf自己写的

3.自己写

1.新建一个Py文件,准备写方法
def MyExceptions(exc, context):
    time = datetime.datetime.today()
    # exc 错误对象
    # context:上下文,有view:当前出错的视图类的对象,args和kwargs视图类方法分组出来的参数,request:当次请求的request对象
    # 只要走到这里,就要记录日志 ,只有错了,才会执行这个函数
    # 记录日志尽量详细
    print(f'当前时间{time},登录用户id:{context.get("request").user.id},用户ip:{context.get("request").META.get("REMOTE_ADDR")},'
          f'请求方式:{context.get("request").method},出错的视图类:{context.get("view")},错误原因:{exc}')
    res = exception_handler(exc, context)
    if res:  # 有值,说明返回了Response 对象,没有值说明返回None
        # 如果是Response 对象说明是drf的异常,已经被处理了,如果是None表明没有处理,就是非drf的异常
        res = Response(data={'code': 888, 'msg': res.data.get('detail', '请联系系统管理员')})
    else:
        # res = Response(data={'code': 999, 'msg': str(exc)})
        # 记录日志
        res = Response(data={'code': 999, 'msg': '系统错误,请联系系统管理员'})


    return res

2.然后项目配置文件记得配置
'EXCEPTION_HANDLER': 'app01.myexception.MyExceptions',

今日注意

1.面向对象——我们可以给类添加属性和值,诸如self.xx= xxx,这样就添加给了类属性,所有根据该类产生的对象都可以点到新的属性拿到值
2.反射——getattr()第三个参数是拿不到就返回的,可以赋值给一个变量,该变量就成了这个参数的值  # 又快忘了,,,
3.类的初始化方法就是__init__,实例化对象时封给了对象的属性
4..pop(),尾部弹出,默认弹出最后一个
5.PageNumberPagination没有count属性,别导错了类

标签:None,day08,return,get,self,permission,request,源码,断点
From: https://www.cnblogs.com/wznn125ml/p/17103466.html

相关文章