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(匿名, 登录用)

三组件组合

posted @ 2019-11-14 14:35  learnacode  阅读(181)  评论(0编辑  收藏  举报