zoukankan      html  css  js  c++  java
  • 1

    drf 源码

    1567937996087

    #  CBV 类型的全是根据反射来的,基于反射实现根据请求方式不同,执行不同的方法
    #  原理 :
        # a. 
        url - view()  - dispath
    
    class StudentsView(View):
        def dispatch(self, request, *args, **kwargs):
            func = getattr(self, request.method.lower())
            ret = func(request, *args, **kwargs)  # 各个函数执行,以及参数 get()....
            return ret
        def get(self,request):
            return HttpResponse('GET')
    

    cbv的运行流程

       def dispatch(self, request, *args, **kwargs):
            ret = super(StudentsView, self).dispatch(request, *args, **kwargs)
            return ret
    
    

    1567938861888

    继承: (多个类公用的功能,为了避免重复)

    面试题

    1. django中间件

      • 中间件最多可以写几个方法:5个
      • process_request view response exception
      • render_template
    2. 执行流程

    3. 中间件做过什么?

      权限

      用户登陆验证

      django csrf_token 怎么实现的?(面试题)

      在view里面 : request view 都是在请求前

      • from django.views.decorators.csrf import csrf_exempt
        
      • csrf_excmpt免除csrf请求

      • 1567939934152

        process_view方法

        检查是否被@csrf_exempt(免除csrf认证)

        去请求体或cookie重获取token

        为什么是view,view之后才是请求执行,才能装饰器

    CBV小知识点:

    csrf时加到单独方法无效

    class:...
    # @csrf_exempt  # 这么加不行
    # @method_decorator(csrf_exempt)
    def post(self, request):
        return HttpResponse('POST')
    

    必须加到dispath里面

     @method_decorator(csrf_exempt)
        def dispatch(self, request, *args, **kwargs):
            func = getattr(self, request.method.lower())
            ret = func(request, *args, **kwargs)  # 各个函数执行,以及参数 get()....
            return ret
    

    或者类上

    @method_decorator(csrf_exempt, name='dispatch')
    class StudentsView(View):
    

    这样写可以少些一个 distpatch 方法

    总结:

    • 本质 : 基于反射来实现
    • 流程 : 路由 view dispatch (反射)
    • csrf_token取消 不从中间件, 要加到类上或者dispatch csrf_exempt
    • 扩展
      • csrf
        • 基于中间件的proces_view
        • 装饰器给单独函数设置或者不适用

    restful 规范 (建议)

    分类记

    1.rest规范
    
    请求是一类:
    
    ​ 使用HTTPs协议 域名 版本 资源(名词) 过滤
    
    请求头: method
    
    响应:
    
    ​ 状态码 错误处理 结果集 Hypermedia API
    
    

    开发前后端分离

    a :
    用户管理 http://www.oldboyedu.com/add_user/ ....

    ​ 返回值很多,返回值的时候,不好处理了

    ​ {

    ​ code:666,

    ​ }

    b:

    ​ vue

    ​ $.ajax({})

    #不好, 因为10张表 处理  ,就40条url了
    # 规范 : 10个url , 用method来处理不同的
    

    基于fbv写

    # 1 根据method不同进行不同的处理
    def order(request):
        if request.method == "GET":
            return HttpResponse('获取')
        if request.method == "add":
            return HttpResponse('获取')
        if request.method == "del":
            return HttpResponse('获取')
        if request.method == "update":
            return HttpResponse('获取')
    

    基于cbv写

    class orderView(View):
        def get(self,request,*args,**kwargs):
            return HttpResponse('获取')
        def post(self,request,*args,**kwargs):  # 创建
            return HttpResponse('获取')
        def put(self,request,*args,**kwargs):   # 更新
            return HttpResponse('获取')
        def delete(self,request,*args,**kwargs):
            return HttpResponse('获取')
    

    RESTful 规范

    1. API与用户的通信协议,总是使用HTTPs协议

    2. 域名上区分(解决跨域问题):

      https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)

      ​ www.luffycity.com

      ​ 子域名上区分 api.luffycity.co

      http://www.luffycity.com/api/v2/salary

    3. 版本(就像bootstrap2/3/4,都有用)

      url方式:

      ​ www.luffycity.com
      ​ www.luffycity.com/api
      版本方式
      ​ www.luffycity.com/api/v1/#

    4. 路径(面向资源编程)

      视网络上任何东西都是资源,均使用名词表示(可复数)

    5. method

      • GET :从服务器取出资源(一项或多项)
      • POST :在服务器新建一个资源
      • PUT :在服务器更新资源(客户端提供改变后的完整资源)
      • PATCH :在服务器更新资源(客户端提供改变的属性)
      • DELETE :从服务器删除资源
    6. 过滤

      通过在url上传参的形式传递搜索条件

    7. 状态码

      200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
      201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
      202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
      204 NO CONTENT - [DELETE]:用户删除数据成功。
      400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
      401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
      403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
      404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
      406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
      410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
      422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
      500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
      
      更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
      
    8. 错误处理

      ​ 状态码是4xx时,应返回错误信息,error当做key。

      {
          ``error: ``"Invalid API key"
      }
      
    9. 返回结果

      针对不同操作,服务器向用户返回的结果应该符合以下规范。

      GET ``/``collection:返回资源对象的列表(数组)
      GET ``/``collection``/``resource:返回单个资源对象
      POST ``/``collection:返回新生成的资源对象
      PUT ``/``collection``/``resource:返回完整的资源对象
      PATCH ``/``collection``/``resource:返回完整的资源对象
      DELETE ``/``collection``/``resource:返回一个空文档
      
    10. Hypermedia API

      RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

      {``"link"``: {
        ``"rel"``:   ``"collection https://www.example.com/zoos"``,
        ``"href"``:  ``"https://api.example.com/zoos"``,
        ``"title"``: ``"List of zoos"``,
        ``"type"``:  ``"application/vnd.yourformat+json"
      }}
      

    以上可以不遵循, 视情况而定

    restful api 规范都有什么? 谈谈你对他的认知

    伪装成一个老手,讲故事

    在使用他的时候,有些适应,有些不用 ,出现问题了,跨域了,

    解决: cors

    ​ jsonp ..

    在聊天, 不好,然后用了那个

    2.djangorestframework 框架

    pip3 install djangorestframework

    带token可以执行操作

    1567998500855

    1567998490695

    from rest_framework.views import APIView
    from rest_framework.authentication import BaseAuthentication
    from rest_framework import  exceptions
    # 登录认证基于这个类实现
    # dispatch->reuqest(封装)->认证(initial)->per():request.user->user->获取认证对象-->类对象--->触发异常或者是token(正常)  # 写了一个My的类,写了一个auth_calssess , 有的话就找本类的,没有就去父类里找
    class Myauthentication(object):
        def authenticate(self,request):
            token = request._request.GET.get('token')
            if not token:
                raise exceptions.AuthenticationFailed('用户认证失败')
            return ('alex',None)
        def authenticate_header(self,val):
            pass
    class DogView(APIView):
        # authentication_classes = [BaseAuthentication, ]  # 获取的是类的对象,
        authentication_classes = [Myauthentication, ]  # 获取的是类的对象,
    
        # self.dispatch
        def get(self, request, *args, **kwargs):
            ret = {
                'code': 1000,
                'msg': 'xxx'
            }
            return HttpResponse(json.dumps(ret), status=201)
    
        def post(self,request,*args,**kwargs):
            return HttpResponse('创建dog')
        def put(self,request,*args,**kwargs):
            return HttpResponse('更新dog')
        def delete(self,request,*args,**kwargs):
            return HttpResponse('删除dog')
    
    

    基于这个类做的认真

    dispatch

    一.认证

    a.认证

    源码流程搞明白:搞不明白,加注释,入口

    dispatch, 不仅会用,还知道原理

    s7129

    今日内容: 1 认证 2 权限 3 节流(访问频率控制) 4 版本

    1568000733364

    用到用户认证,需要先写一个类,然后,在写一个那个对象[,]

    也可以全局用到

    1568015600768

    b.基本使用认证组件

    • 解决:
      • a.创建两张表
      • b.用户登陆(返回token并保存到数据库)

    c. 源码

    1568015825015

    1568017683422

    authentication_classes = [Authtication,]
    #  self.dispatch 入口
    
    def dispatch(self, request, *args, **kwargs):
       self.args = args
            self.kwargs = kwargs
            # 对原生的request进行加工(追加)
            # Request(request,parsers=self.get_parsers(),   authenticators=self.get_authenticators(),  negotiator=self.get_content_negotiator(),    parser_context=parser_context)
            # request(原生request,  [BaseAuthentication对象,], )
            # 获取原生request, self._request
            # 获取认证类的对象, request.authenticator
            # 1.封装Request
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
            
     try:
                # 2. 认证成功,走反射
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)
    		
    		# 认证失败,抛异常
            except Exception as exc:
                response = self.handle_exception(exc)
    
    
    def initialize_request(self, request, *args, **kwargs):
        parser_context = self.get_parser_context(request)
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    
        def get_authenticators(self):
            # self.authentication_classes = [foo,bar]
            return [auth() for auth in self.authentication_classes]
    #对象,然后,自己写了这个类,和
    
    

    如下:

    from rest_framework.authentication import BaseAuthentication

    # 重写这两个方法
    def authenticate(self, request):
    def authenticate_header(self, request): # pass就行
    
    
    authentication_classes = [Authtication,]
    
    # 用了drf , 对于重用的功能
    class Authtication(object):
        def authenticate(self,request):
            token = request._request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise exceptions.AuthenticationFailed('用户认证失败')
            # 在rest_framework内部会将整个两个字段赋值给request,以供后续操作使用
            return (token_obj.user, token_obj)
    
        def authenticate_header(self, request):
            pass
    
    def initial(self, request, *args, **kwargs):
    # 4.实现认证
        self.perform_authentication(request)
    
    def perform_authentication(self, request):
    	request.user
    

    request.user在 Request.py里

    @property
    def user(self):
    	if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                # 获取认证对象, 进行一步步的认证
                self._authenticate()
        return self._user
    
    [BaseAuthentication对象,]
     def _authenticate(self):
            # [BaseAuthentication对象,]
            # 循环认证类的所有对象
            for authenticator in self.authenticators:
                try:
                    # 执行认证类的authenticate方法
                    # 1.如果auth方法抛出异常,self._not_auth()执行
                    # 2.有返回值,必须得是元祖(request.user, request.auth)
                    # 3.返回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.auth = user_auth_tuple
                    return
    
            self._not_authenticated()
    
        def _not_authenticated(self):
            self._authenticator = None
            if api_settings.UNAUTHENTICATED_USER:
                self.user = api_settings.UNAUTHENTICATED_USER() # AnonymousUser
            else:
                self.user = None
            if api_settings.UNAUTHENTICATED_TOKEN:
                self.auth = api_settings.UNAUTHENTICATED_TOKEN() # None
            else:
                self.auth = None
    
    

    执行了函数,函数就返回了

    return (token_obj.user, token_obj) ,根据这里写的异常和元祖返回

    d.源码配置 p=19

    把写的类,返回元祖的认证的,返回异常的,写到setting里

    然后所有的类默认都加了,不用在一个个单独写了

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.FirstAuthtication', 'api.utils.auth.Authtication' ]
    }
    
    

    全局都能用,局部不要用

    class UserInfoView(APIView):
        """订单相关业务"""
        # 类似于中间件, 装饰器的处理restframework的方法,认证登录方法
        authentication_classes = []
        def get(self, request, *args, **kwargs):
            return HttpResponse('用户信息')
    
    

    http://127.0.0.1:8000/api/v1/order/?token=851c293fe784dea90e079aad5d498f42

    {
        "code": 1000,
        "msg": null,
        "data": {
            "1": {
                "name": "xifu",
                "age": 18,
                "gender": "男",
                "content": "..."
            },
            "2": {
                "name": "xifu2",
                "age": 18,
                "gender": "男",
                "content": "..."
            }
        }
    }
    
    

    http://127.0.0.1:8000/api/v1/order/

    用户未登录

    在最后 , _not_authenticated函数中

    if api_settings.UNAUTHENTICATED_USER:
        self.user = api_settings.UNAUTHENTICATED_USER() # AnonymousUser
    else:
        self.user = None
    
    if api_settings.UNAUTHENTICATED_TOKEN:
        self.auth = api_settings.UNAUTHENTICATED_TOKEN() # None
    
    
    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
    
    
    def reload_api_settings(*args, **kwargs):
        setting = kwargs['setting']
        if setting == 'REST_FRAMEWORK':
            api_settings.reload()
    
    
    

    所以在settings里全局使用设置, USER , 和 TOKEN

    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

    REST_FRAMEWORK = {
        # 全局使用的认证类
        # 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', 'app01.utils.auth.Authtication' ],
        'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', ],
    
        "UNAUTHENTICATED_USER":    None, # 匿名用户, request.user= None
        # 'UNAUTHENTICATED_TOKEN':None ,  # request.auth = None
    }
    
    
    

    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

    在rest_framework中settings里

    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
    def reload_api_settings(*args, **kwargs):
        setting = kwargs['setting']
        if setting == 'REST_FRAMEWORK':
            api_settings.reload()
    
    

    匿名是request.user = None

    不是模态对话框

    1568021678285

    1568021689065

    浏览器提供的,填完之后, 加密

    1568021725261

    基于django来实现的

    不会用这些的,都是自定义来实现的

    e.内置认证类

    • 认证类, 必须继承:from rest_framework.authentication import BaseAuthentication

    • 其他认证类:

    梳理:

    1. 使用

      • 创建类:继承BaseAuthentication 实现auth..方法

      • 返回值:

        • None,下一个认证来执行
        • 抛异常 exceptions.AuthenticationFailed('用户认证失败') #from rest_framework import exceptions
        • (元素1,元素2) #元素1赋值给request.user;元素2赋值给request.auth
      • 局部使用

      • class Authtication(object):
            def authenticate(self,request):
                token = request._request.GET.get('token')
                token_obj = models.UserToken.objects.filter(token=token).first()
                if not token_obj:
                    raise exceptions.AuthenticationFailed('用户认证失败')
                # 在rest_framework内部会将整个两个字段赋值给request,以供后续操作使用
                return (token_obj.user, token_obj)
        
            def authenticate_header(self, request):
                pass
        
      • 全局使用

      • REST_FRAMEWORK = {
            # 全局使用的认证类
            # 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', 'app01.utils.auth.Authtication' ],
            'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', ],
        
            "UNAUTHENTICATED_USER": None,  # (没登录) 匿名用户, request.user= None   # 返回的是函数()所以写匿名函数
            # 'UNAUTHENTICATED_TOKEN':None ,  # request.auth = None
        }
        
        

    2.源码流程

    先走dispatch,request封装,走initial,找所有的对象,执行方法(两个)可以在setting中设置,但是api.settings为什么和settings相关联,不太懂

    • dispatch

      • 封装request

        获取定义的认证类(全局/局部),通过列表生成式创建对象

    • initial

      • perform_authentication
        • request.user(内部循环...)

    后面的东西和这里的认证流程是一样的

    二.权限

    认证. user_type 有1234的时候可以访问

    1568022484654

    问题:不用视图不用权限可以访问

    也是通过dispatch到4,

    到check_permissions

    遇到的问题1

    在设置超级vip时,返回的值是1
    if request.user.user_type != 1:
        return HttpResponse('无权访回')
        
    AttributeError: 'NoneType' object has no attribute 'user_type'    
    

    因为我权限里给他设置了

        # 全局使用的认证类
       # 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', 'app01.utils.auth.Authtication' ],
         'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', ],
    

    返回的是空,而不是request.user和 request.auth,所以返回的是空

    设置超级vip

    根据每一个对象的user_type判断, 有的给他一个超级vip,别的默认不处理,(这里1是vip)

    if request.user.user_type != 1:
        return HttpResponse('无权访回')
    
    

    放入类中封装

    # 认证过之后就开始权限认证了
    class MyPermission(object):
        # self.dispatch
        def has_permission(self, request, view):
            # 把权限写入类里面
            if request.user.user_type != 1:
                return False
            return True  # 有权访问
    
    
    class OrderView(APIView):    
        # 局部权限
        permission_classes = [MyPermission, ]
    
    

    1568034481217

    认证的所有的开启全局的之后,就没法处理auth了吗?

    class MyPermission1(object):
        def has_permission(self, request, view):
            # 把权限写入类里面
            if request.user.user_type == 3:
                return False
            return True  # 有权访问
    
    
    class UserInfoView(APIView):
        """订单相关业务(普通用户/vip)"""
        # 类似于中间件, 装饰器的处理restframework的方法,认证登录方法
        # authentication_classes = []
        permission_classes = [MyPermission1, ]
        def get(self, request, *args, **kwargs):
            return HttpResponse('用户信息')
    
    

    完成了基本的权限了

    看源码流程

    dispatch
    initial
    check_permissions
    		      # [权限类的对象,权限类的对象,]
            for permission in self.get_permissions():
                # 通过per...为ture, 则not per..为false,不走, 而为false,则为ture,				走里面,抛异常
                if not permission.has_permission(request, self):
                    self.permission_denied(
                        request, message=getattr(permission, 'message', None)
                    )
                    
    def permission_denied(self, request, message=None):
    """
    If request is not permitted, determine what kind of exception to raise.
    """
            if request.authenticators and not request.successful_authenticator:
                raise exceptions.NotAuthenticated()
            raise exceptions.PermissionDenied(detail=message)                
    

    写成全局的

    utils
    	-auth.py
    	-permission.py
    
    REST_FRAMEWORK = {
    	'DEFAULT_PERMISSION_CLASSES': ['app01.utils.permission.SVIPPermission']  	# 全局的
    }
    
    # 认证过之后就开始权限认证了
    class SVIPPermission(object):
        # self.dispatch
        message = '必须是SVIP才能访问'
        def has_permission(self, request, view):
            # 把权限写入类里面
            if request.user.user_type != 1:
                return False
            return True  # 有权访问
    class MyPermission1(object):
        def has_permission(self, request, view):
            # 把权限写入类里面
            if request.user.user_type == 3:
                return False
            return True  # 有权访问
    
    

    如果写在全局,那么所有的都能用,如果不想用,字段换了就好了

    # permission_classes = [MyPermission1, ]
    
    

    有没有内置的权限呢?

    有默认是return true

    为了规范

    from rest_framework.permissions import BasePermission
    # 按照代码规范应该继承,内置的
    class SVIPPermission(BasePermission):
    
    

    别的权限,都是通过django写的

    1568036241344

    生产的环境,不可能是用默认的东西,都是自己去写(定制,而且级别也更高)

    梳理:

    • 1 . restframework 写权限的时候,最好写到类中,放到他的组件中

    ​ 必须继承:BasePermission,必须实现: has_permission方法

    • 返回值

      return False 	#无权访问
      return True     #有权访问
      
      
    • 局部:

      class UserInfoView(APIView):
          """订单相关业务(普通用户/vip)"""
          permission_classes = [MyPermission1, ] #加上这个
          def get(self, request, *args, **kwargs):
      
      
    • 全局:

      'DEFAULT_PERMISSION_CLASSES': ['app01.utils.permission.SVIPPermission'] 
      
      

    下午:

    三.节流

    1,2 登陆之后才能看, 什么用户(vip还是svip)

    3,访问频率

    限制: 比如1分钟多少次 6次

    保存了一个访问记录的字典 = {身份证号:[12:10:10,12:10:09,12:10:08]}

    # 12:10:10,12:10:09,12:10:08,12:10:07,12:10:06,12:10:05

    12:50:10

    [12:10:10,12:10:09,12:10:08,] 可以清除

    12:10:11

    [12:10:10,12:10:09,12:10:08,] 就是大于3次了,不能了

    根据ip,可以记录

    访问记录 = {用户IP:[....]} //但是ip可以换啊, 没办法, 匿名用户, 爬虫

    注册用户, 可以限制, 所以现在手机号绑定了,但是用户名多了,也没法办 .

    class VisitThrottle(object):
        def allow_request(self, request, view):
            # return True# 可以继续访问 # return False 表示访问频率太高,被限制
            return False
        def wait(self):
            pass
    
    class AuthView(APIView):
        authentication_classes = []
        permission_classes = []
        throttle_classes = [VisitThrottle,]
    
    {
        "detail": "Request was throttled."
    }
    

    错误,显示,没有get方法,用的是post,我一直在访问get,

    {
        "detail": "Method "GET" not allowed."
    }
    

    是因为没有post方法,我在找源码

    wait()等待

      def wait(self):  # 提示,再等多少秒就能访问了
            """
            还需要等多少秒才能访问
            :return:
            """
            ctime = time.time()
            return 60 - (ctime - self.history[-1])   # 当前时间减去最后加入的时间
    
    {
        "detail": "Request was throttled. Expected available in 49 seconds."
    }
    
    

    给你这样显示,返回的值给了内部自定义的 一个 , 也可以自定义吧

    import time
    VISIT_RECORD = {}
    
    class VisitThrottle(object):
        """60秒内只访问3次"""
        def allow_request(self, request, view):
            # 1.获取用户ip
            remote_addr = request.META.get('REMOTE_ADDR')
            ctime = time.time()
            # remote_addr = request._request.get('REMOTE_ADDR')
            if remote_addr not in VISIT_RECORD:
                VISIT_RECORD[remote_addr] = [ctime, ]
                return True
            history = VISIT_RECORD.get(remote_addr)
            self.history = history
     
    		while history and history[-1] < ctime - 60:  # 有的值, 很可能是1千年以前,不作数,删掉  
                history.pop()				# 60 < ctime - history[-1] # 留着 
                               		 # 当大于60的时候,说明是一分钟以内,可以留着
    
            if len(history) < 3:
                # 访问把,当前时间加上去
                history.insert(0, ctime)
                return True
    
            # return True 表示可以访问
            # return False 表示访问频率太高,被限制
            return False
    
        def wait(self):  # 提示,再等多少秒就能访问了
            """
            还需要等多少秒才能访问
            :return:
            """
            ctime = time.time()
            return 60 - (ctime - self.history[-1])   # 当前时间减去最后加入的时间
    
    

    访问放到{} ,一刷新没了! 放到django的缓存中

    节流源码

    dispatch-->check_throttles

    def check_throttles(self, request):  
      throttle_durations = []
            for throttle in self.get_throttles():
                if not throttle.allow_request(request, self):   #所以自己写的allow_request.返回True和false , True时不走(not True)下面(异常),false(not flase) 走下面异常
                     throttle_durations.append(throttle.wait())  #原来是将wait()传给wait了
    
            if throttle_durations:
                self.throttled(request, max(throttle_durations)) # 这里用最大值,比较好,用最后一个也行, 反正是列表里的一个,时间wait()返回的时间
    
       def throttled(self, request, wait):
            """
            If request is throttled, determine what kind of exception to raise.
            """
            raise exceptions.Throttled(wait)
            # 然后wait返回的时间拼接成时间, 异常 , wait是用的反射吗?  也不是啊, 是protity方法吗
    
    
    class Throttled(APIException):
        status_code = status.HTTP_429_TOO_MANY_REQUESTS
        default_detail = _('Request was throttled.')
        extra_detail_singular = _('Expected available in {wait} second.')
        extra_detail_plural = _('Expected available in {wait} seconds.')
        default_code = 'throttled'
    
        def __init__(self, wait=None, detail=None, code=None):
            if detail is None:
                detail = force_text(self.default_detail)
            if wait is not None:
                wait = math.ceil(wait)
                detail = ' '.join((
                    detail,
                    force_text(ngettext(self.extra_detail_singular.format(wait=wait),
                                        self.extra_detail_plural.format(wait=wait),
                                        wait))))
            self.wait = wait
            super().__init__(detail, code)
    

    找到了 {wait}的时间了 , 但是为什么是wait,不是wait(),不是方法吗?

    ​ throttle_durations.append(throttle.wait()) #原来是将wait()传给wait了

    在check_throttles函数中

    全局配置(从Apiview里找参数,加到settings里)

    utils
    auth.py
    permission.py
    throttle.py

    'DEFAULT_THROTTLE_CLASSES': ['app01.utils.throttle.VisitThrottle']  # 所有的都是基于ip做的
    

    基于内置的可以修改

    get_cache_key 没写, 预留给你写的

    class SimpleRateThrottle(BaseThrottle):
    	cache = default_cache   # django内置的缓存
    	timer = time.time
        cache_format = 'throttle_%(scope)s_%(ident)s'
        scope = None
        THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
        def __init__(self)
        def get_cache_key(self, request, view):
            raise NotImplementedError('.get_cache_key() must be overridden') # 重写
        def get_rate(self)
        def parse_rate(self, rate)
        def allow_request(self, request, view)
        def throttle_success(self)
        def throttle_failure(self)
        def wait(self)
    

    别的都给写了,说明不需要定制,死的, key可以根据自定义 ip 身份证等

    distatch -> allow_requset() ,

    cache 是缓存

    cache = default_cache   # 缓存  不知道怎么处理的
    def __init__(self):
    	if not getattr(self, 'rate', None):
    	# '3/m'
    		self.rate = self.get_rate()         # 3次 , 60秒
        self.num_requests, self.duration = self.parse_rate(self.rate)
    
     def parse_rate(self, rate):
            """
            Given the request rate string, return a two tuple of:
            <allowed number of requests>, <period of time in seconds>
            """
            if rate is None:
                return (None, None)
            num, period = rate.split('/')
            num_requests = int(num)
            duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]  # 也可以 '3/minu'取零个,都能取到值
            return (num_requests, duration)   # 3次 60 秒
    
    
       def allow_request(self, request, view):      
            if self.rate is None:
                return True
    
            self.key = self.get_cache_key(request, view)  # ip
            if self.key is None:
                return True
            self.history = self.cache.get(self.key, [])   # 根据ip的key取值
            self.now = self.timer()
            # Drop any requests from the history which have now passed the
            # throttle duration
            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()
    
    throttle_success   # 成功之后	
    throttle_failure	# 没有节流
    wait	# 等的时间都有
    

    缓存看不懂 (内部django的)

    # 用他自己的, 通过改配置文件,就可以控制频率,封装的更好
    class VisitThrottle(SimpleRateThrottle):
        scope = 'luffy'  # 当key用的
        def get_cache_key(self, request, view):
            return self.get_ident(request)
    
    
        'DEFAULT_THROTTLE_RATES': {
            'Luffy': '3/m',   # 3次, 每分钟
            "LuffyUser": '10/m',
        }
    

    按照用户的访问频率限制

    'DEFAULT_THROTTLE_CLASSES': ['app01.utils.throttle.UserThrottle'],  # 只登录用户的
    
    # 用户访问的频率
    class UserThrottle(SimpleRateThrottle):
        scope = 'LuffyUser'
        def get_cache_key(self, request, view):
            return request.user.username
    
    from app01.utils.throttle import VisitThrottle
    class AuthView(APIView):
        throttle_classes = [VisitThrottle, ]        # VisitThrottle
    

    梳理:

    a.基本使用

    - 类 , 继承 : BaseThrottle , 实现 : allow_request, wait 
    - 类 , 继承 : SimpleRateThrottle, 实现 : get_cache_key  scope = 'LuffyUser'(配置文件中的key)
    
    

    b.局部认证

    ​ 用于用户登录认证

    ​ throttle_classes = [VisitThrottle, ] # VisitThrottle

    c.全局

    'DEFAULT_THROTTLE_RATES': {
        'Luffy': '3/m',   # 3次, 每分钟
        "LuffyUser": '10/m',
    }
    
    

    认证的时候, 有一个reqeust.user好像麻烦点的

    面试题:

    1568092029446

    今日作业

    昨天

    • 1.中间件

    • 2.Csrf

    • 3.cBV

    • 4.规范

      10条规范

      认识

    • 5.djangoreac-ramework

      如何验证(基于数据摩实现用户认证)

      深码流程(向对象回赎流程)

    今天上午:

    • 1.中间件

    • 2.crf漂

    • 3.cst10范

    • 4.面向对象

    • 5.d1ango请求声明,期

    • 6.dang请求声明同期(包含 reet framework框架

      PS: dispatch

    • 7.rest framework 认证流程(封装Request)

    • 8.rest framework 权限流程

    • 9.rest framework 节流流程

    文档的形式记录下来

    写文本:(所有的)

    代码:

    ​ 认证demo

    ​ 权限demo(用户类型不同,权限不同)

    ​ 节流demo(匿名, 登录用)

    三组件组合

  • 相关阅读:
    “上海名媛群”事件,我来说几句
    急于脱手商业地产的酒店式公寓,让我在无锡买了房
    40年产权的商业地产,个人投资者决不能碰
    产权分割商铺,太坑人!
    我的第二故乡 – 武汉
    我的第二故乡
    Consul踢除失效服务和移除Node节点
    合并2个数组为1个无重复元素的有序数组--Go对比Python
    当Prometheus遇到混沌工程
    测试流程规范--测试准入、准出、停止标准、bug优先级定义
  • 原文地址:https://www.cnblogs.com/Doner/p/11857085.html
Copyright © 2011-2022 走看看