首页 > 编程语言 >drf三大认证源码/全局异常捕获

drf三大认证源码/全局异常捕获

时间:2023-02-08 21:26:20浏览次数:32  
标签:get self request 认证 源码 user 执行 三大 drf

三大认证源码分析

权限类的执行源码

已知在drf中ApiView 在执行视图类方法之前先进行了3大认证

1.在执行视图类方法前 先进行了三大认证  在apiview中的dispatch方法中 497行左右
try:
   self.initial(request, *args, **kwargs)
   # 只有通过了3大认证 才会执行下面的代码
  
   if request.method.lower() in self.http_method_names:
       handler = getattr(self, request.method.lower(),
                         self.http_method_not_allowed)
        
2.查看initial方法发现:
        self.perform_authentication(request)
        self.check_permissions(request)
        # 执行了该方法 检查权限
        self.check_throttles(request)
        

3.查看check_permissions方法
def check_permissions(self, request):
for permission in self.get_permissions():
    # 发现这里又执行了get_permissions()方法,查看该方法发现
    # 这里其实就是获取到了我们设定的权限类并执行了
    if not permission.has_permission(request, self):
      # 执行我们重写的has_permission如果返回ture就是false这里取反了
      # ture就不需要执行if里面的代码了 直接循环下一个权限类
      # 如果是false那这里取反就是true那就要执行if内代码
        self.permission_denied(
          # 执行.permission_denied
          # 结果raise抛异常
            request, 
            message=getattr(permission, 'message', None),
            code=getattr(permission, 'code', None)
                )
        
#总结:
APIView---dispatch----initial---check_permissions
里面取出配置在视图类上的权限类,实例化得到对象,一个个执行对象的has_permission方法,如果返回False,就直接结束,进行permission_denied方法

如果视图类上不做权限类配置  默认使用配置文件中的api_settings.DEFAULT_PERMISSION_CLASSES

认证类的执行源码

1.查看initial方法发现:
        self.perform_authentication(request)
  			request.user
        # 该方法返回了一个request.user 分析request.user
    
2.查看request类中的user方法
  def user(self):
    # 这里面的self是 request类的对象
    if not hasattr(self,'_user'):
      with wrap_attributeerrors():
        self._authenticate()
    return self._user 
  
  
3.执行了 self._authenticate()方法
  在request类中的_authenticate()方法
  
def _authenticate(self):
   for authenticator in self.authenticators:
      # self.authenticators就是我们配置的认证类 通过for循环来执行认证
      # 是request的类在初始化生成对象的时候传入的authenticators
       try:
           user_auth_tuple = authenticator.authenticate(self)
           # 这里执行了我们配置的认证类,然后我们的认证类会返回用户对象
           # 和token 如果有返回两个就成功走下面的代码,结束了for循环
           # 如果没有 返回的none 那就继续循环执行下一个认证类 
       except exceptions.APIException:
           # 这里捕获了我们抛的异常
           self._not_authenticated()
           # 这里执行了没有认证通过
           raise
       if user_auth_tuple is not None:
        # 通过拿到我们认证类给的 user对象 然后加入了self中
        # 这样就明白了为什么 request中可以通过.user方式拿到对象
           self._authenticator = authenticator
           self.user, self.auth = user_auth_tuple
            # 解压赋值 将我们返回的对象给了self.user,我们返回的token给了self.auth
           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(),
            # 这里传入了authenticators,所以之前才可以self.authenticators
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
      
      
      
 # 总结:
	1 配置在视图类上的认证类,会在执行视图类方法之前执行,在权限认证之前执行
  不然权限类拿不到user对象 无法核实权限
  2 自己写的认证类,可以返回数据对象/数据 2个值 或 None
  多认证类时返回none会执行下一个认证类
  3 认证通过后可以从request.user 取出当前登录用户(前提是你要在认证类中返回)
  

频率类的执行源码

1.在执行视图类方法前 先进行了三大认证  在apiview中的dispatch方法中 497行左右
try:
   self.initial(request, *args, **kwargs)
   # 只有通过了3大认证 才会执行下面的代码
  
   if request.method.lower() in self.http_method_names:
       handler = getattr(self, request.method.lower(),
                         self.http_method_not_allowed)
        
2.查看initial方法发现:
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
        # 查看权限类执行流程

 3.# APIView 的352行
    def check_throttles(self, request):
        throttle_durations = []
        # 先定义了一个空列表
        for throttle in self.get_throttles():
          # self.get_throttles()执行我们配置的频率类对象
            if not throttle.allow_request(request, self):
              # 用我们的频率类来执行.allow_request方法,如果返回false就是超频了,无法再接着往下走了
              # 如果是true 没有超频就可以接着走
                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

基于APIView编写分页

定制分页字段

from rest_framework.viewsets import ViewSet
编写分页功能需要继承 ViewSetMixin 和 apiview或其子类

class books(ViewSet):
    def list(self, request):
        books = Book.objects.all()
        paginator = CommonLimitOffsetPagination()
        # 1 实例化得到一个分页类的对象
        page = paginator.paginate_queryset(books, request, self)
        # 2 调用分页类对象的paginate_queryset方法来完成分页,返回的page是 要序列化的数据,分页好的
        if page is not None:
            serializer = BookSerializer(instance=page, many=True)
            return Response({
                'total': paginator.count,
                # 这是第几页的意思
                'next': paginator.get_next_link(),
                 # 下一页地址
                'previous': paginator.get_previous_link(),
                 # 上一页地址
                'results': serializer.data
                 # 本页数据
            })
            # 3 返回数据,
  

全局异常处理

应用下创建处理异常文件 exception.py

目标:无论是主动抛异常还是程序运行错误,都返回固定的格式,并记录日志

# APIView--->dispatch--->三大认证,视图类的方法,如果出了异常,会被异常捕获,捕获后统一处理
# drf 内置了一个函数,只要上面过程出了异常,就会执行这个函数,这个函数只处理的drf的异常
# 但是我们代码中的异常 程序报错无法捕获
# 判断处理的结果(返回值)response,有值代表drf已经处理了,None代表需要自己处理

先将异常处理交给rest_framework.views的exception_handler去处理

def common_exception(exc, context):
    res = exception_handler(exc, context)
    times = datetime.datetime.now()
    # 获取错误发生时间
    user_obj = context.get('request')
    # 拿到错误对象
    view = context.get('view')
    # 获取发生错误的视图函数
    ip = user_obj.META.get('REMOTE_ADDR')
    # 获取请求的id
    method = user_obj.method
    # 获取请求方式
    user_id = user_obj.user.pk
    # 获取用户id

    print(f'时间:{times}--ip:{ip}--请求方式--{method}--错误模块:{view}--错误信息:{exc}--用户id:{user_id}')

    if res:
        # 如果有值说明是drf的异常 已经被处理了
        res = Response(data={'code': '9999', 'msg': res.data.get('detail', '请联系系统管理员')})
    else:
        # 没值说明的是django的错误
        res = Response(data={'code': '8888', 'msg': '系统错误请联系系统管理员'})
    return res

  
 
sttings.py文件--------------
  
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['app01.authentications.LoginAuth'],
    'DEFAULT_PERMISSION_CLASSES': ['app01.permissions.CommonPermission'],
    'DEFAULT_THROTTLE_RATES': {
        'booksListThrottling': '5/m',

    },
    'EXCEPTION_HANDLER': 'app01.exceptions.common_exception',
    # 在配置文件中配置一下刚刚写的错误方法 
}
  
  

标签:get,self,request,认证,源码,user,执行,三大,drf
From: https://www.cnblogs.com/moongodnnn/p/17103322.html

相关文章