1. 程式人生 > >python_restframework(頻率組件)

python_restframework(頻率組件)

key either 管理 ror prop 一次 remote available not

BaseThrottle
(1) 取出訪問者ip
(2) 判斷當前ip不在訪問字典裏,添加進去,並且直接返回True,表示第一次訪問,在字典裏,繼續往下走
(3) 循環判斷當前ip的列表,有值,並且當前時間減去列表的最後一個時間大於60s,把這種數據pop掉,這樣列表中只有60s以內的訪問時間,
(4) 判斷,當列表小於3,說明一分鐘以內訪問不足三次,把當前時間插入到列表第一個位置,返回True,順利通過
(5) 當大於等於3,說明一分鐘內訪問超過三次,返回False驗證失敗

1、分發display

def dispatch(self, request, *args, **kwargs):
    try:
        # 進入初始化
        self.initial(request, *args, **kwargs)

2、 drf初始化方法

APIview下的方法
def initial(self, request, *args, **kwargs):
    # 認證
    self.perform_authentication(request)
    # 進入權限
    self.check_permissions(request)
    # --> 頻率
    self.check_throttles(request)

3、頻率模塊

def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    # 循環的是列表
    for throttle in self.get_throttles():
        # 返回結果true or false, false就繼續執行
        if not throttle.allow_request(request, self):
            # 視圖類的三個參數, self, request,  
            # throttle.wait(): 最後wait返回的數字
            self.throttled(request, throttle.wait())

3.1、for throttle in self.get_throttles():

def get_throttles(self):
    """
    Instantiates and returns the list of throttles that this view uses.
    """
    # 跟權限組件一樣, 這裏循環 return出去的也是一個列表
    return [throttle() for throttle in self.throttle_classes]

3.2、if 判斷

# 當返回為false時,說明該用戶或訂單無此權限,   
# not false為true 繼續往下執行, not true就是 false, 不執行下面代碼
if not throttle.allow_request(request, self):

3.3、throttle.allow_request

# 如果是false 就直接返回這個錯誤了
def allow_request(self, request, view):
    """
    Return `True` if the request should be allowed, `False` otherwise.
    """
    raise NotImplementedError(‘.allow_request() must be overridden‘)

4、BasePermission

# 繼承基礎的認證權限, 如果忘了要定義哪個類 直接在這裏看也OK

from rest_framework.throttling import BaseThrottle
    # 自定義的組件
    def allow_request(self, request, view):
        raise NotImplementedError(‘.allow_request() must be overridden‘)

    def get_ident(self, request):
        xff = request.META.get(‘HTTP_X_FORWARDED_FOR‘)
        remote_addr = request.META.get(‘REMOTE_ADDR‘)
        num_proxies = api_settings.NUM_PROXIES

        if num_proxies is not None:
            if num_proxies == 0 or xff is None:
                return remote_addr
            addrs = xff.split(‘,‘)
            client_addr = addrs[-min(num_proxies, len(addrs))]
            return client_addr.strip()

        return ‘‘.join(xff.split()) if xff else remote_addr
    # 最後要返回的時間或者其它
    def wait(self):
        return None

5、定義一個權限

class MyPermission(BasePermission):
    # 前臺返回的錯誤
    message = "您沒有權限, 請先聯系下管理員增加權限"
    # 獲取權限
    def has_permission(self,request, view):
        # 認證組件, 返回的request.user
        print("permission: ", request.user.permission)
        if request.user.permission > 1:
            # 如果大於就是true, 在第3步,if not true 等於false就不執行它了
            return True
        return False

6、頻率組件

class FirstThrottle(BaseThrottle):
    get_ip = {}

    def __init__(self):
        self.history = None
        self.ip = None
        self.ctime = time.time()

    def allow_request(self, request, view):
        :param request: 瀏覽器請求過來的數據
        :param view: apiview視圖
        :return: true or false

        # 1、取出訪問者的IP
        client_ip = request.META.get("REMOTE_ADDR")
        self.ip = client_ip
        # 2、判斷不存在的話添加到字典 並將時間也一並添加進去
        if client_ip not in self.get_ip:
            self.get_ip[client_ip] = [self.ctime, ]
            return True
        # 獲取當前IP的訪問時間記錄
        self.history = self.get_ip[client_ip]

        # 3、 開始循環判斷, 如果最後一個大於60秒就直接幹掉
        while self.history and self.ctime - self.history[-1] > 60:
            self.history.pop()

        if len(self.history) < 3:
            self.history.insert(0, self.ctime)
            return True
        return False

    def wait(self):
        last_time = self.ctime - self.history[-1] - 10
        if last_time == 0:
            self.get_ip[self.ip].clear()
        return last_time

7、全局使用頻率

# settings.py文件中定義, 所有的組件都可以放在這裏

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": [
        ‘app01.myauth.FirstThrottle‘,  # 全局使用權限
    ]
}

7、局部使用

類中直接使用 
    throttle_classes = [FirstThrottle, ]

8、局部禁用

類中直接使用 
    throttle_classes = []

SimpleRateThrottle

使用組件中自帶的頻率控制組件

先在settings.py中定義限制頻率的範圍
REST_FRAMEWORK={
    "DEFAULT_THROTTLE_RATES": {
        "thro_rate": "10/m"
    }
}

1、進入頻率

class SimpleRateThrottle(BaseThrottle):
    cache = default_cache
    # 獲取時間
    timer = time.time
    cache_format = ‘throttle_%(scope)s_%(ident)s‘
    # 這個是在setting中設置的 DEFAULT_THROTTLE_RATES的字典key, 必須得定義
    scope = None
    THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES

    # 初始化,
    def __init__(self):
        # 首先就是先判斷 rate是否為空, 如果是false為空,就進入self.get_rate 
        if not getattr(self, ‘rate‘, None):
            # 直接輸出錯誤
            self.rate = self.get_rate()
        # 如果上一步通過,就繼續進入這裏 9.2    
        self.num_requests, self.duration = self.parse_rate(self.rate)
        # 也就是說執行完9.2之後 獲取到的結果就是
        # self.num_requests, self.duration = (10,60)

1.1、get_rate

def get_rate(self):
    # scope 這個值在類中必須被定義成 settings中定義的值 如thro_rate
    if not getattr(self, ‘scope‘, None):
        msg = ("You must set either `.scope` or `.rate` for ‘%s‘ throttle" %
        self.__class__.__name__)
        raise ImproperlyConfigured(msg)
    try:
        # 在配置文件中 將thro_rate 取出, 返回 10/m
        return self.THROTTLE_RATES[self.scope]
    except KeyError:
        msg = "No default throttle rate set for ‘%s‘ scope" % self.scope
        raise ImproperlyConfigured(msg)

2、當初始化通過

? 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>
    """
    # 這個是在setting中設置的 DEFAULT_THROTTLE_RATES的字典設置為空,就直接返回none,none
    if rate is None:
        return (None, None)
    # 這裏的rate就是就是get_rate取出來的10/m 然後切割它
    num, period = rate.split(‘/‘)
    num_requests = int(num)
    # 定義如果是m就是60秒,然後字典中直接取值這裏是m取出來的就是60
    duration = {‘s‘: 1, ‘m‘: 60, ‘h‘: 3600, ‘d‘: 86400}[period[0]]
    # 最後返回它倆
    return (num_requests, duration)

3、類中調用get_cache_key

def get_cache_key(self, request, view):
    """
    # 應返回可用於限制的唯一緩存鍵。
    Should return a unique cache-key which can be used for throttling.
    # 必須要重寫, 否則調用SimpleRateThrottle也會直接報錯
    Must be overridden.

    May return `None` if the request should not be throttled.
    """
    raise NotImplementedError(‘.get_cache_key() must be overridden‘)

4、實例

class FirstThrottle(SimpleRateThrottle):
    # 這裏會調用 self.get_rate那個函數,返回的就是 10/m了
    scope = "thro_rate"

    # 如果不重新定義就會報錯, 因為它得從緩存中找出 ip地址
    def get_cache_key(self, request, view):
        # 返回空也行, 也會有倒計時
        return self.get_ident(request)
        # "detail": "Request was throttled. Expected available in 56 seconds."

5、中文顯示錯誤日誌

5.1、流程的前3步

def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    for throttle in self.get_throttles():
        if not throttle.allow_request(request, self):
            # 如果不存在 就進入到 throttled中
            self.throttled(request, throttle.wait())

5.2、throttled 錯誤提示

def throttled(self, request, wait):
    """
    If request is throttled, determine what kind of exception to raise.
    """
    # 返回錯誤信息
    raise exceptions.Throttled(wait)

5.3、重寫exceptions方法

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.‘

5.4、實例

from app01.SelfThrottle import FirstThrottle
from rest_framework import exceptions

class Thor(APIView):
    # 局部使用
    throttle_classes = [FirstThrottle, ]

    def get(self, request, *args, **kwargs):
        return HttpResponse("ok")

    # 需要註意的是 這裏需要在視圖類的重寫方法,或繼承
    def throttled(self, request, wait):
        class Myerrors(exceptions.Throttled):
            default_detail = "超出頻率限制"
            extra_detail_singular = ‘請 {wait} 秒後在訪問.‘
            extra_detail_plural = ‘請 {wait} 秒後在訪問.‘

        raise Myerrors(wait)

python_restframework(頻率組件)