六、限流组件
限制某个视图在某个时间段内被同一个用户访问的次数
6.1限流组件的简单应用
1)安装django-redis
pip3 install django-redis
2)在settings.py中注册cache
python">#缓存数据库redis配置
CACHES={"default":{"BACKEND":"django_redis.cache.RedisCache","LOCATION":"redis://"+redis_host+":"+redis_port, #redis主机地址和端口,数据在local_settings中"OPTIONS":{"CLIENT_CLASS":"django_redis.client.DefaultClient","CONNECTION_POOL_KWARGS":{"max_connections":1000,"encoding":"utf-8"},"PASSWORD":redis_password #redis密码,数据在local_settings中}}
}
3)编写限流类
python">#ext.throttlefrom rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cacheclass Mythrottle(SimpleRateThrottle):scope='xxx' #限流名称,可自定义#限流策略('xxx'是scope,'5/m'代表每份钟访问次数不超过5次,s:1,m:60,h:60*60,d:60*60*24)THROTTLE_RATES = {'xxx':'5/m'}#这个cache其实就是上面在settings.py中注册的rediscache = default_cachedef get_cache_key(self, request, view):#获取唯一标识if request.user:#如果是已登入用户,ident取用户idident=request.user.pkelse:#如果是未登入用户,ident取用户IPident=self.get_ident(request)#返回格式化以后的唯一标识return self.cache_format % {'scope':self.scope,'ident':ident}
3)在视图中应用限流
python">class LoginView(MyAPIView):#用户登入,不需要认证,不需要任何权限authentication_classes = []permission_classes = []#应用限流throttle_classes = [Mythrottle,]def post(self,request):name=request.data.get('name')password=request.data.get('password')print(name,password)user_obj=models.User.objects.filter(name=name,password=password).first()if not user_obj:#用户名密码错误return Response({'status':False,'errMsg':'用户名或密码错误'})token=str(uuid.uuid4())user_obj.token=tokenuser_obj.save()return Response({'status':True,'token':token})
4)效果
5)全局配置
python">#settings.py
REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES':{'xxx':'5/m','ooo':'3/m'},
}
python">#限流类
class Mythrottle(SimpleRateThrottle):#这个scope的值,必须在全局配置中已定义scope='xxx'这个THROTTLE_RATES在限流类中就不需要写了# THROTTLE_RATES = {'xxx':'5/m'}cache = default_cachedef get_cache_key(self, request, view):if request.user:ident=request.user.pkelse:ident=self.get_ident(request)return self.cache_format % {'scope':self.scope,'ident':ident}
6.2自定义错误信息
限流的错误信息实际上写在rest_framework.exceptions.Throttled中
python">class Throttled(APIException):#错误信息status_code = status.HTTP_429_TOO_MANY_REQUESTSdefault_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_str(self.default_detail)if wait is not None:wait = math.ceil(wait)detail = ' '.join((detail,force_str(ngettext(self.extra_detail_singular.format(wait=wait),self.extra_detail_plural.format(wait=wait),wait))))self.wait = waitsuper().__init__(detail, code)
因此我们只需要在执行限流时修改一下这里的错误信息即可
自定义一个类,继承APIView,并修改错误信息
python">from rest_framework import exceptions
from django.utils.translation import gettext_lazy as _
class MyAPIView(APIView):def __init__(self):exceptions.Throttled.default_detail = _('请求被限制,')exceptions.Throttled.extra_detail_singular = _('{wait}秒后可继续访问.')exceptions.Throttled.extra_detail_plural = _('{wait}秒后可继续访问.')
视图类继承上面创建的类MyAPIView
python">class LoginView(MyAPIView):#用户登入,不需要认证,不需要任何权限authentication_classes = []permission_classes = []#限流throttle_classes = [Mythrottle,]def post(self,request):name=request.data.get('name')password=request.data.get('password')print(name,password)user_obj=models.User.objects.filter(name=name,password=password).first()if not user_obj:#用户名密码错误return Response({'status':False,'errMsg':'用户名或密码错误'})token=str(uuid.uuid4())user_obj.token=tokenuser_obj.save()return Response({'status':True,'token':token})
效果: