六、限流组件
限制某个视图在某个时间段内被同一个用户访问的次数
6.1限流组件的简单应用
1)安装django-redis
pip3 install django-redis
2)在settings.py中注册cache
#缓存数据库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)编写限流类
#ext.throttle
from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache
class 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中注册的redis
cache = default_cache
def get_cache_key(self, request, view):
#获取唯一标识
if request.user:
#如果是已登入用户,ident取用户id
ident=request.user.pk
else:
#如果是未登入用户,ident取用户IP
ident=self.get_ident(request)
#返回格式化以后的唯一标识
return self.cache_format % {'scope':self.scope,'ident':ident}
3)在视图中应用限流
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=token
user_obj.save()
return Response({'status':True,'token':token})
4)效果
5)全局配置
#settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES':{'xxx':'5/m','ooo':'3/m'},
}
#限流类
class Mythrottle(SimpleRateThrottle):
#这个scope的值,必须在全局配置中已定义
scope='xxx'
这个THROTTLE_RATES在限流类中就不需要写了
# THROTTLE_RATES = {'xxx':'5/m'}
cache = default_cache
def get_cache_key(self, request, view):
if request.user:
ident=request.user.pk
else:
ident=self.get_ident(request)
return self.cache_format % {'scope':self.scope,'ident':ident}
6.2自定义错误信息
限流的错误信息实际上写在rest_framework.exceptions.Throttled中
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_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 = wait
super().__init__(detail, code)
因此我们只需要在执行限流时修改一下这里的错误信息即可
自定义一个类,继承APIView,并修改错误信息
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
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=token
user_obj.save()
return Response({'status':True,'token':token})
效果: