断点调试/认证/权限/频率-源码分析/基于APIView编写分页/异常处理

2023-05-25,,

内容概要

断点调试
认证/权限/频率-源码分析
基于APIView编写分页
异常处理

断点调试

# 程序以 debug模式运行,可以在任意位置停下,查看当前情况下变量数据的变化情况

# pycharm 来调试程序
-以debug形式运行
-在左侧空白处,点击加入断点 (红圈)
-step over 单步调试
-step into 进入到函数内部运行
-快速调到下一个断点,绿色箭头

认证,权限,频率源码分析

权限类的执行流程

​ 编写权限类(permission)局部使用,配置在视图类中的,就会执行权限类的has_permission方法,完成权限校验

​ 我在之前分析APIView视图源码的时候分析过

drf中的APIView在执行视图类方法之前,执行了 权限/认证/频率的校验

-497行左右, self.initial(request, *args, **kwargs)---》执行3大认证

因为是Initial方法执行的三大认证我们进到initial方法中看一下

# APIView类的399行左右:
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)

我们可以看到 有check_permission方法,那么我们可以猜测就是这个方法完成了权限的校验

# APIView的326 左右
def check_permissions(self, request):
# self.get_permissions()----》[CommonPermission(),]
# permission 是我们配置在视图类上权限类的对象
for permission in self.get_permissions():
# 权限类的对象,执行has_permission,这就是为什么我们写的权限类要重写has_permission方法
# self 是视图类的对象,就是咱们自己的的权限类的has_permission的view参数
if not permission.has_permission(request, self):
# 如果return 的是False,就会走这里,走这里是,没有权限
# 如果配了多个权限类,第一个没过,直接不会再执行下一个权限类了
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None))

我们就可以查看以下for循环的对象输出的是什么

# APIView的274行左右  get_permissions
def get_permissions(self):
# self.permission_classes 是咱们配置在视图类上的列表,里面是一个个的权限类,没加括号
# permission_classes = [CommonPermission]
# [CommonPermission(),] 本质返回了权限类的对象,放到列表中
return [permission() for permission in self.permission_classes]

权限类源码总结

# -APIView---dispatch----》initial---》倒数第二行---》self.check_permissions(request)
从中取出配置在视图类上的权限类,实例化产生对象,一个个执行对象中的has_permission方法,如果返回False,就直接结束,不再继续往下执行,权限就认证通过
如果视图类上不配做权限类:permission_classes = [CommonPermission],会使用配置文件的api_settings.DEFAULT_PERMISSION_CLASSES
优先使用项目配置文件,其次使用drf内置配置文件

认证源码分析

​ 与权限源码一样,都是继承了APIView及其子类,才会到执行视图类之前执行其中的三大认证

# APIView类的399行左右:
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_throdttles(request)

我们到self.perform_authentication(request)看看源码到底做了什么

    def perform_authentication(self, request):
request.user
# 看起来就是一个属性,但是其实他是被property装饰器包装的方法
'''
因为是request.user,我们需要去Request类中寻找user方法
'''
# Request类的user方法 219行左右
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
# self 是Request的对象,找Request类的self._authenticate() 373 行
def _authenticate(self):
# self.authenticators 我们配置在视图类上认证类的一个个对象,放到列表中
# Request类初始化的时候,传入的
for authenticator in self.authenticators:
try:
# 返回了两个值,第一个是当前登录用户,第二个的token,只走这一个认证类,后面的不再走了
# 可以返回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是当次请求的新的Request的对象
#self.auth=token
self.user, self.auth = user_auth_tuple
return self._not_authenticated() # self.authenticators 去Request类的init中找 152行左右
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
.....
self.authenticators = authenticators or ()
.....
# 什么时候调用Reqeust的__init__?---》APIVIew的dispatch上面的492行的:request = self.initialize_request(request, *args, **kwargs)-----》385行----》
def initialize_request(self, request, *args, **kwargs):
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)

认证总结

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

频率源码分析

​ 与权限源码一样,都是继承了APIView及其子类,才会到执行视图类之前执行其中的三大认证

# APIView 的352行
def check_throttles(self, request):
throttle_durations = []
#self.get_throttles() 配置在视图类上的频率类的对象,放到列表中
# 每次取出一个频率类的对象,执行allow_request方法,如果是False,频率超了,不能再走了
# 如果是True,没有超频率,可以直接往后
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) # 总结:
-我们写的频率类:继承BaseThrottle,重写allow_request,在内部判断,如果超频了,就返回False,如果没超频率,就返回True

自定义频率类

class Super_Throttle(BaseThrottle):
# (1)取出访问者ip (重写 all_request方法)
def __init__(self):
self.history = None
ask_dict = {}
def allow_request(self, request, view):
import time
user_ip = request.META.get('REMOTE_ADDR')
# (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走 {ip地址:[时间1,时间2,时间3,时间4]}
if not user_ip in self.ask_dict:
self.ask_dict[user_ip] = [time.time()]
return True
time_list = self.ask_dict.get(user_ip,[])
# (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
while time_list and time.time() - time_list[-1]:
self.history.pop()
# (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
if len(self.history) < 3:
self.history.insert(0,time.time())
return True
else:
# (5)当大于等于3,说明一分钟内访问超过三次,返回False验
return False

SimpleRateThrottle源码分析

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

# simpleRateThrottle --- > allow_request
def allow_request(self, request, view):
# 这里就是通过配置文件和scope取出 频率限制是多少,比如一分钟访问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 3600
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,3600)
return (num_requests, duration)

断点调试/认证/权限/频率-源码分析/基于APIView编写分页/异常处理的相关教程结束。

《断点调试/认证/权限/频率-源码分析/基于APIView编写分页/异常处理.doc》

下载本文的Word格式文档,以方便收藏与打印。