首页 > 其他分享 >drf8

drf8

时间:2023-02-08 22:12:11浏览次数:36  
标签:None drf8 APIView self request 源码 def

今日内容概要

  • 认证 权限 频率源码分析
  • 基于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

相关文章