今日内容概要
- 认证 权限 频率源码分析
- 基于APIView编写分页
- 异常处理
今日内容详细
认证 权限 频率源码分析
权限源码分析
之前分析APIView源码可以知道在dispatch中执行了三大认证
在APIView源码的第497行左右
self.initial(request, *args, **kwargs)
在APIView源码的第399行左右
def initial(self, request, *args, **kwargs):
# 这个是认证组件执行的位置
self.perform_authentication(request)
# 权限组件执行的位置
self.check_permissions(request)
# 频率组件执行的位置
self.check_throttles(request)
在APIView源码的第326行左右
def check_permissions(self, request):
# get_permissions方法返回的是包含配置的权限类产生的对象的列表
for permission in self.get_permissions():
# 权限类的对象调用has_permission方法 也就是为什么我们自定义权限类要重写它
if not permission.has_permission(request, self):
# 如果return的False 那么就会走里面
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
在APIView源码的第169行左右
def permission_denied(self, request, message=None, code=None):
# 该方法会主动抛出异常 也就是如果配了多个权限类 只要第一个没过 那么后面的就不执行了
raise exceptions.PermissionDenied(detail=message, code=code)
总结:
从APIView切入---->dispatch方法---->initial方法--->check_permissions方法--->循环获取get_permissions方法返回的包含所有权限类对象的列表--->一个个执行has_permission方法--->如果返回的是False 那么就是没权限 直接结束 不再往下执行 反之通过
认证源码分析
在APIView源码的第399行左右
def initial(self, request, *args, **kwargs):
# 这个是认证组件执行的位置
self.perform_authentication(request)
# 权限组件执行的位置
self.check_permissions(request)
# 频率组件执行的位置
self.check_throttles(request)
在APIView源码的第316行左右
def perform_authentication(self, request):
# 看起来没什么 其实user是被伪装成属性的方法 去request类的源码中找
request.user
在Request类源码的第220行左右
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
# 从这行入手
self._authenticate()
return self._user
在Request类源码的373行左右
def _authenticate(self):
# authenticators返回的是包含我们配置的认证类产生的一个个对象的列表
# 认证类是在Request类初始化的时候传入的 具体可以看图
for authenticator in self.authenticators:
try:
# authenticate会返回两个值 第一个是当前登录用户 第二是token
# 可以返回None 那么就会继续执行下一个认证类
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
# 判断这个认证类有没有返回None
if user_auth_tuple is not None:
self._authenticator = authenticator
# 解压赋值 也就是之后可以通过request.user可以获取到当前登录用户
self.user, self.auth = user_auth_tuple
# 如果返回的不是None 那么就只会执行第一个认证类
return
self._not_authenticated()
频率源码分析
在APIView源码的第399行左右
def initial(self, request, *args, **kwargs):
# 这个是认证组件执行的位置
self.perform_authentication(request)
# 权限组件执行的位置
self.check_permissions(request)
# 频率组件执行的位置
self.check_throttles(request)
在APIView源码的第352行左右
def check_throttles(self, request):
throttle_durations = []
# get_throttles方法返回的是包含频率类对象的列表
for throttle in self.get_throttles():
# 执行频率类对象的allow_request方法
if not throttle.allow_request(request, self):
# 如果返回的是False 那么会走里面 往列表里面添加数据
throttle_durations.append(throttle.wait())
# 如果该列表有值 也就是超出频率 那么就会做一些操作
if throttle_durations:
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
自定义频率类
import time
from rest_framework.throttling import BaseThrottle
class CommonThrottle(BaseThrottle):
visit_dict = {}
def __init__(self):
# 给每个对象都设一个历史访问时间属性
self.history = None
def allow_request(self, request, view):
# 取出访问者ip
ip = request.META.get('REMOTE_ADDR')
ctime = time.time
# 判断当前ip不在访问字典里
if ip not in self.visit_dict:
# 如果不在添加进去 并将访问时间添加到访问字典中
# 组成 {ip:[访问时间1] 的形式
self.visit_dict[ip] = [ctime()]
return True
# 给每个访问对象都添加历史访问时间列表到历史访问属性中
self.history = self.visit_dict[ip]
# 判断当前时间减去访问时间列表最后一个时间是否大于于60
if ctime() - self.history[-1] > 60:
# 大于60s则删除该数据 让访问时间列表内都是60s内的记录
self.history.pop()
if len(self.history) >= 3:
# 当大于等于3,说明一分钟内访问超过三次,返回False验证失败
return False
# 当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,这样访问时间列表最后一位永远是最开始访问的时间
self.history.insert(0, ctime())
return True
# 重写该方法可以在提示的时候显示剩余秒数
def wait(self):
ctime = time.time
return 60 - (ctime() - self.history[-1])
基于APIView编写分页
class BookView(ViewSet):
# 由于使用了自动路由 所以是做了映射的 重写list方法即可
def list(self, request):
# 查看ListModelMixin源码 参考基于GenericAPIView是如何写的分页 模仿即可
books = Book.objects.all()
# 实例化得到一个分页类对象
paginator = CommonPageNumberPagination()
# 调用分页类对象的paginate_queryset方法来分页 返回的是分页号的数据
page = paginator.paginate_queryset(books, request, self)
if page is not None:
# 将分页号的数据交给序列化类
ser = BookSerializer(instance=page, many=True)
# 参考分页类的get_paginated_response方法是如何返回的
return Response({
'count': paginator.page.paginator.count,
'next': paginator.get_next_link(),
'previous': paginator.get_previous_link(),
'results': ser.data
})
异常处理
在执行三大认证及视图类方法的时候 如果出现了异常 都会被异常捕获 然后统一处理
但是非drf的异常都不会被处理
那么如何实现 所有异常都会被捕获 并且返回统一格式呢
写一个函数 内部处理异常 在配置文件中配置一下即可
def common_exception_handler(exc, context):
# exc是错误对象
# context是上下文 有view:当前出错的视图类的对象 args和kwargs视图类分组出来的参数 以及当次请求的request对象
# exception_handler是处理异常的函数 如果是drf错误 那么返回Response对象 不是则返回None
res = exception_handler(exc, context
if res:
# 有值说明是drf异常 我们处理一下返回格式即可
res = Response({'code': 10001, 'msg': res.data.get('detail')})
else:
# 是非drf错误 也处理一下返回格式
res = Response({'code': 10002, 'msg': str(exc)})
return res
在配置文件中配置一下
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler',
}
这样无论是drf异常还是非drf异常都会被捕获到
标签:None,drf8,APIView,self,request,源码,def
From: https://www.cnblogs.com/lzjjjj/p/17103489.html