DRF频率组件与上面两个组件也是几乎一样,只是用来做判断的逻辑不一样而已,下面使用自定义方式实现对ip地址进行访问频率控制
局部频率组件 自定义频率组件类 组件类位置:app.untils.app_throttle.VisitThottle
组件类代码:
1 2 3 4 5 6 7 8 9 from rest_framework.throttling import SimpleRateThrottleclass VisitThottle (SimpleRateThrottle ): rate = '5/m' def get_cache_key (self, request, view ): return self.get_ident(request)
rate代表访问评率,上面表示每分钟五次,get_cache_key是必须存在的,它的返回值告诉当前频率控制组件要使用什么方式区分访问者(比如ip地址)。
引入自定义频率组件类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from app.untils.app_throttle import VisitThottleclass BooksView (ModelViewSet ): queryset = BookModel.objects.all () serializer_class = BookSerialize authentication_classes = [UserAuth] permission_classes = [UserPermission] throttle_classes = [VisitThottle]
使用postman接口测试 1 2 3 4 # 如果一分钟访问超过5 次,响应内容如下: { "detail" : "Request was throttled. Expected available in 59 seconds." }
view.py所有代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 from django.http import JsonResponsefrom rest_framework.views import APIViewfrom rest_framework.viewsets import ModelViewSetfrom app.models import User as UserModelfrom app.models import UserToken as UserTokenModelfrom app.models import Book as BookModelfrom app.untils import get_tokenfrom app.api_serialize import BookSerializefrom app.untils.authentication import UserAuthfrom app.untils.permission import UserPermissionfrom app.untils.app_throttle import VisitThottleclass UserView (APIView ): def post (self, request ): response = dict () recev_data = {'username' , 'password' } if recev_data.issubset(set (request.data)): userInfo = dict () for item in recev_data: userInfo[item] = request.data.get(item) user_obj = UserModel.objects.filter (**userInfo).first() if user_obj: access_token = get_token.generater_token() UserTokenModel.objects.update_or_create(user=user_obj, defaults={ 'token' :access_token }) response['status_code' ] = 200 response['status_message' ] = '恭喜你,登录成功' response['access_token' ] = access_token response['user_role' ] = user_obj.get_level_display() else : response['status_code' ] = 201 response['status_message' ] = '用户名或密码错误' return JsonResponse(response) class BooksView (ModelViewSet ): queryset = BookModel.objects.all () serializer_class = BookSerialize authentication_classes = [UserAuth] permission_classes = [UserPermission] throttle_classes = [VisitThottle]
全局组件 创建自定义频率类 1 2 3 4 5 6 7 8 9 10 11 from rest_framework.throttling import SimpleRateThrottleclass VisitThottle (SimpleRateThrottle ): scope = 'visit_rate' def get_cache_key (self, request, view ): return self.get_ident(request)
setting配置全局限制频率 1 2 3 4 5 6 7 8 9 10 11 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES' : ['app.untils.app_throttle.VisitThottle' ], 'DEFAULT_THROTTLE_RATES' : { 'visit_rate' : '3/m' } }
使用postman接口测试 1 2 3 4 # 如果一分钟访问超过5 次,响应内容如下: { "detail" : "Request was throttled. Expected available in 59 seconds." }
超过速率修改响应消息: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import timeimport mathfrom rest_framework import exceptionsclass MyException (exceptions.Throttled): default_detail = '连接次数过多' extra_detail_plural = extra_detail_singular = '请在{wait}秒内访问' def __init__ (self, wait=None , detail=None , code=None ): super ().__init__(wait=wait, detail=detail, code=code) class VisitThrottle (): user_visit_information = dict () visited_times = 1 period = 60 allow_times_per_minute = 5 first_time_visit = True def allow_request (self, request, view ): self.request_host = request_host = request.META.get("REMOTE_ADDR" ) current_user_info = self.user_visit_information.get(request_host, None ) if not self.__class__.first_time_visit: self.user_visit_information[request_host][0 ] += 1 current_visit_times = self.user_visit_information[request_host][0 ] if current_visit_times > self.allow_times_per_minute: if self._current_time - current_user_info[1 ] <= self.period: if len (current_user_info) > 2 : current_user_info[2 ] = self._time_left else : current_user_info.append(self._time_left) view.throttled = self.throttled return None else : self.__class__.first_time_visit = True if self.first_time_visit: self.__class__.first_time_visit = False self._initial_infomation() return True def wait (self ): return self.period - self.user_visit_information[self.request_host][2 ] def throttled (self, request, wait ): raise MyException(wait=wait) @property def _current_time (self ): return time.time() @property def _time_left (self ): return math.floor(self._current_time - self.user_visit_information.get(self.request_host)[1 ]) def _initial_infomation (self ): self.user_visit_information[self.request_host] = [self.visited_times, self._current_time]