From FBV to a CBV (access frequency limit)

For example, we have a user Big Wheel lottery functions, users need to be specified within an hour can only draw three times, and that this time limit on the frequency of access interface is particularly important

In fact, restframework already provides component frequency limit for us

First stroke about the process to APIview request:

  as_view -> dispatch -> initialize_request -> initial -> perform_authentication -> check_permissions -> check_throttles (achieved here is frequency limit)

 1     def initial(self, request, *args, **kwargs):
 2         """
 3         Runs anything that needs to occur prior to calling the method handler.
 4         """
 5         self.format_kwarg = self.get_format_suffix(**kwargs)
 6 
 7         # Perform content negotiation and store the accepted info on the request
 8         neg = self.perform_content_negotiation(request)
 9         request.accepted_renderer, request.accepted_media_type = neg
10 
11         # Determine the API version, if versioning is in use.
12         version, scheme = self.determine_version(request, *args, **kwargs)
13         request.version, request.versioning_scheme = version, scheme
14 
15         # Ensure that the incoming request is permitted
16 
17         # 身份验证
18         self.perform_authentication(request)
19         # 权限验证
20         self.check_permissions(request)
21         # 访问频率限制
22         self.check_throttles(request)

 

That check_throttles in the end to do anything at all?

1     def check_throttles(self, request):
2         """
3         Check if request should be throttled.
4         Raises an appropriate exception if the request is throttled.
5         """
6         for throttle in self.get_throttles():
7             if not throttle.allow_request(request, self):
8                 self.throttled(request, throttle.wait())

And check_permissions actually very similar, divided into the following steps:

  1. self.get_throttles () to get a list of registered throttle class by deriving the formula, and returns instantiation

1     def get_throttles(self):
2         """
3         Instantiates and returns the list of throttles that this view uses.
4         """
5         return [throttle() for throttle in self.throttle_classes]

  2. throttle.allow_request DESCRIPTION throttle must be achieved allow_request class methods, and the return value True to allow access right, it performs the next cycle, examined under a frequency control object

    According to the previous routine, throttle assembly should have a basic throttle class, find it:

    

  

 1 class BaseThrottle(object):
 2     """
 3     Rate throttling of requests.
 4     """
 5 
 6     def allow_request(self, request, view):
 7         """
 8         Return `True` if the request should be allowed, `False` otherwise.
 9         """
10         raise NotImplementedError('.allow_request() must be overridden')
11 
12     def get_ident(self, request):
13         """
14         Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
15         if present and number of proxies is > 0. If not use all of
16         HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
17         """
18         xff = request.META.get('HTTP_X_FORWARDED_FOR')
19         remote_addr = request.META.get('REMOTE_ADDR')
20         num_proxies = api_settings.NUM_PROXIES
21 
22         if num_proxies is not None:
23             if num_proxies == 0 or xff is None:
24                 return remote_addr
25             addrs = xff.split(',')
26             client_addr = addrs[-min(num_proxies, len(addrs))]
27             return client_addr.strip()
28 
29         return ''.join(xff.split()) if xff else remote_addr
30 
31     def wait(self):
32         """
33         Optionally, return a recommended number of seconds to wait before
34         the next request.
35         """
36         return None

 

  3. 如果返回值为False,就执行 self.throttled

# 抛出Throttled异常
1
def throttled(self, request, wait): 2 """ 3 If request is throttled, determine what kind of exception to raise. 4 """ 5 raise exceptions.Throttled(wait)

    

# Throttled异常类
1
class Throttled(APIException): 2 status_code = status.HTTP_429_TOO_MANY_REQUESTS 3 default_detail = _('Request was throttled.') 4 extra_detail_singular = 'Expected available in {wait} second.' 5 extra_detail_plural = 'Expected available in {wait} seconds.' 6 default_code = 'throttled' 7 8 def __init__(self, wait=None, detail=None, code=None): 9 if detail is None: 10 detail = force_text(self.default_detail) 11 if wait is not None: 12 wait = math.ceil(wait) 13 detail = ' '.join(( 14 detail, 15 force_text(ungettext(self.extra_detail_singular.format(wait=wait), 16 self.extra_detail_plural.format(wait=wait), 17 wait)))) 18 self.wait = wait 19 super(Throttled, self).__init__(detail, code)

    

  

  实现:

    一般来说,接口如果不做登录限制,那就会允许匿名用户和已登录用户都能访问。所以这个接口就要考虑能对匿名用户和登录用户都进行访问频率限制:

    思路:

    已经登录用户可以根据身份做判断,固定时间内,同一个用户的身份只能访问限定次数

    未登录用户可通过IP地址判断,对同一个IP的请求进行限制

    

 1 class TestThrottle(BaseThrottle):
 2     ctime = time.time
 3 
 4     def get_ident(self, request):
 5         """
 6         根据用户IP和代理IP,当做请求者的唯一IP
 7         Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
 8         if present and number of proxies is > 0. If not use all of
 9         HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
10         """
11         xff = request.META.get('HTTP_X_FORWARDED_FOR')
12         remote_addr = request.META.get('REMOTE_ADDR')
13         num_proxies = api_settings.NUM_PROXIES
14 
15         if num_proxies is not None:
16             if num_proxies == 0 or xff is None:
17                 return remote_addr
18             addrs = xff.split(',')
19             client_addr = addrs[-min(num_proxies, len(addrs))]
20             return client_addr.strip()
21 
22         return ''.join(xff.split()) if xff else remote_addr
23 
24     def allow_request(self, request, view):
25         """
26         是否仍然在允许范围内
27         Return `True` if the request should be allowed, `False` otherwise.
28         :param request: 
29         :param view: 
30         :return: True,表示可以通过;False表示已超过限制,不允许访问
31         """
32         # 获取用户唯一标识(如:IP)
33 
34         # 允许一分钟访问10次
35         num_request = 10
36         time_request = 60
37 
38         now = self.ctime()
39         ident = self.get_ident(request)
40         self.ident = ident
41         if ident not in RECORD:
42             RECORD[ident] = [now, ]
43             return True
44         history = RECORD[ident]
45         while history and history[-1] <= now - time_request:
46             history.pop()
47         if len(history) < num_request:
48             history.insert(0, now)
49             return True
50 
51     def wait(self):
52         """
53         多少秒后可以允许继续访问
54         Optionally, return a recommended number of seconds to wait before
55         the next request.
56         """
57         last_time = RECORD[self.ident][0]
58         now = self.ctime()
59         return int(60 + last_time - now)
1 class MemberPrograms(APIView):
2     throttle_classes = [TestThrottle, ]
3 
4     def get(self, request):
5         programs = MemberProgram.objects.all().values()
6         return JsonResponse(list(programs), safe=False)

 

  测试:

  

  

 

 

 

  

   

    

 

     

  

  

   

 

 

 

Guess you like

Origin www.cnblogs.com/wangbaojun/p/11013375.html