七 解析器
解析器的作用:
-用来解析前台传过来的数据编码方式 urlencoded:form表单:name=lqz&age=18 formdata :上传文件:--dadfgdgag-- json:json格式 {"name":"lqz"} -解析器取的顺序 1 视图类中 2 django总settings里取 3 drf默认的配置文件取 —全局配置 在setting中: REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser', # 'rest_framework.parsers.FormParser', ] } -局部使用: 在视图类中: from rest_framework.parsers import JSONParser,MultiPartParser,FormParser parser_classes = [JSONParser,FormParser]
例子
解析器 局部配置 from rest_framework.parsers import JSONParser class Book(APIView): parser_classes = [JSONParser, ] def get(self,request,*args,**kwargs): return HttpResponse('ok') def post(self,request): print(request.data) return HttpResponse('post') from rest_framework.parsers import JSONParser,MultiPartParser,FormParser class Book(APIView): parser_classes = [JSONParser,FormParser] def get(self,request,*args,**kwargs): return HttpResponse('ok') def post(self,request): print(request.data) return HttpResponse('post')
八、认证组件
1、校验是否登陆
-作用:校验是否登陆 -首先定义一个类,继承BaseAuthentication,写一个方法:authenticate,在方法内部,实 证过程,认证通过,返回None或者两个对象(user,auth),这两个对象,在视图类的request中可以取出来 from rest_framework.authentication import BaseAuthentication class myAuthen(BaseAuthentication): def authenticate(self, request): token = request.query_params.get('token') ret = models.UserToken.objects.filter(token=token).first() if ret: # return ret.user, ret # 要写多个认证类,这个地返回None # 最后一个认证类,返回这俩值 return ret.user, ret else: raise AuthenticationFailed('您没有登陆') -局部使用:在视图类中(可以写多个) authentication_classes = [myAuthen, ] -全局使用: "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
2、正常非drf认证代码,update_or_create意思是有则更新,无则创建
class User(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
pwd=models.CharField(max_length=32,null=True)
mychoice=((1,'普通用户'),(2,'超级用户'),(3,'宇宙用户'))
usertyle=models.IntegerField(choices=mychoice,default=1)
class UserToken(models.Model):
user=models.OneToOneField(to=User,to_field='nid')
token=models.CharField(max_length=64)
def get_token(username): import hashlib import time md = hashlib.md5() # update内必须传bytes格式 md.update(username.encode('utf-8')) md.update(str(time.time()).encode('utf-8')) return md.hexdigest() class Login(APIView): def post(self, request): response = MyResponse() name = request.data.get('name') pwd = request.data.get('pwd') user = models.User.objects.filter(name=name, pwd=pwd).first() if user: response.msg = '登陆成功' # 登陆成功,返回一个随机字符串,以后在发请求,都携带这个字符串 token = get_token(name) response.token = token # 把随机字符串保存到数据库,有就更新,没有就创建 # ret=models.UserToken.objects.update_or_create(user_id=user.id,kwargs={'token':token}) ret = models.UserToken.objects.update_or_create(user=user, defaults={'token': token}) else: response.msg = '用户名或密码错误' response.status = 101 return JsonResponse(response.get_dic)
3、drf的认证组件用法,return user或者auth
class myAuthen(): def authenticate(self,request): token = request.query_params.get('token') ret = models.UserToken.objects.filter(token=token).first() if ret: return ret.user,ret else: raise AuthenticationFailed('您咩有登陆') def authenticate_header(self,value): pass from rest_framework.request import Request from app01 import myserial
class Book(APIView):
authentication_classes = [myAuthen, ]
def get(self, request):
response = MyResponse()
print(request.user.name)
print(request.auth.token)
# 必须登陆才能访问
books = models.Book.objects.all()
ret = myserial.BookSer(instance=books, many=True)
response.msg = '查询成功'
response.data = ret.data
return JsonResponse(response.get_dic, safe=False)
4、先建个auth.py,存入认证组价的功能
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from app01 import models class myAuthen(BaseAuthentication): def authenticate(self, request): token = request.query_params.get('token') ret = models.UserToken.objects.filter(token=token).first() if ret: # return ret.user, ret # 要写多个认证类,这个地返回None # 最后一个认证类,返回这俩值 return ret.user, ret else: raise AuthenticationFailed('您没有登陆')
5、若在setting里配置了全局验证,则全局验证,但是视图函数里的login()不需要,则我们参考了顺序是先找局部,在找setting里。所以需要在视图函数里配置
authentication_classes = [],即不需要验证
class Login(APIView): authentication_classes = [] def post(self, request): response = MyResponse() name = request.data.get('name') pwd = request.data.get('pwd') user = models.User.objects.filter(name=name, pwd=pwd).first() if user: response.msg = '登陆成功' # 登陆成功,返回一个随机字符串,以后在发请求,都携带这个字符串 token = get_token(name) response.token = token # 把随机字符串保存到数据库,有就更新,没有就创建 # ret=models.UserToken.objects.update_or_create(user_id=user.id,kwargs={'token':token}) ret = models.UserToken.objects.update_or_create(user=user, defaults={'token': token}) else: response.msg = '用户名或密码错误' response.status = 101 return JsonResponse(response.get_dic)
6、settings
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser', # 'rest_framework.parsers.FormParser', ], "DEFAULT_AUTHENTICATION_CLASSES": ["app01.auth.myAuthen", ], "DEFAULT_PERMISSION_CLASSES":["app01.auth.myPermission",] }
7、存的token数据一般存在服务器端数据库里,但是存在压力,第一存在redis里,第二种方式就是不存数据库的token验证,及如下所示
def get_token(id,salt='123'): import hashlib md=hashlib.md5() md.update(bytes(str(id),encoding='utf-8')) md.update(bytes(salt,encoding='utf-8')) return md.hexdigest()+'|'+str(id) def check_token(token,salt='123'): ll=token.split('|') import hashlib md=hashlib.md5() md.update(bytes(ll[-1],encoding='utf-8')) md.update(bytes(salt,encoding='utf-8')) if ll[0]==md.hexdigest(): return True else: return False class TokenAuth(): def authenticate(self, request): token = request.GET.get('token') success=check_token(token) if success: return else: raise AuthenticationFailed('认证失败') def authenticate_header(self,request): pass class Login(APIView): def post(self,reuquest): back_msg={'status':1001,'msg':None} try: name=reuquest.data.get('name') pwd=reuquest.data.get('pwd') user=models.User.objects.filter(username=name,password=pwd).first() if user: token=get_token(user.pk) # models.UserToken.objects.update_or_create(user=user,defaults={'token':token}) back_msg['status']='1000' back_msg['msg']='登录成功' back_msg['token']=token else: back_msg['msg'] = '用户名或密码错误' except Exception as e: back_msg['msg']=str(e) return Response(back_msg) from rest_framework.authentication import BaseAuthentication class TokenAuth(): def authenticate(self, request): token = request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if token_obj: return else: raise AuthenticationFailed('认证失败') def authenticate_header(self,request): pass class Course(APIView): authentication_classes = [TokenAuth, ] def get(self, request): return HttpResponse('get') def post(self, request): return HttpResponse('post')
九 权限组件
1、校验用户是否有权限访问
-作用:校验用户是否有权限访问 -因为是在认证通过才执行,所以可以取出user class myPermission(): message = '不是超超级用户,查看不了' def has_permission(self, request, view): if request.user.usertyle != 3: return False else: return True -局部使用 在视图类中:permission_classes=[myPermission,] -全局使用 在setting中: "DEFAULT_PERMISSION_CLASSES":["app01.auth.myPermission",]
2、choices=的写法,一般用性别等不常用的常识
class User(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) pwd=models.CharField(max_length=32,null=True) mychoice=((1,'普通用户'),(2,'超级用户'),(3,'宇宙用户')) usertyle=models.IntegerField(choices=mychoice,default=1) class UserToken(models.Model): user=models.OneToOneField(to=User,to_field='nid') token=models.CharField(max_length=64)
3、eg
from rest_framework.permissions import BasePermission class myPermission(BasePermission): message = '不是超超级用户,查看不了' def has_permission(self, request, view): if request.user.usertyle != 3: return False else: return True
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':[
'rest_framework.parsers.JSONParser',
# 'rest_framework.parsers.FormParser',
],
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.auth.myAuthen", ],
"DEFAULT_PERMISSION_CLASSES":["app01.auth.myPermission",]
}
from app01.auth import myAuthen from app01.auth import myPermission class Book(APIView): # authentication_classes = [myAuthen, ] # permission_classes=[myPermission,] def get(self, request): response = MyResponse() print(request.user.name) print(request.auth.token) # 必须登陆才能访问 books = models.Book.objects.all() ret = myserial.BookSer(instance=books, many=True) response.msg = '查询成功' response.data = ret.data return JsonResponse(response.get_dic, safe=False)
十、频率组件
1、参考原码写的符合频率组件logic的代码
class MyThro(): VISIT_RECORD = {} # VISIT_RECORD = {ip:[时间1,时间2],ip2:[],ip3:[当前时间]} def __init__(self): self.history = None def allow_request(self, request, view): # (1)取出访问者ip # print(request.META) ip = request.META.get('REMOTE_ADDR') import time ctime = time.time() # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问 if ip not in self.VISIT_RECORD: self.VISIT_RECORD[ip] = [ctime, ] return True self.history = self.VISIT_RECORD.get(ip) # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间, while self.history and ctime - self.history[-1] > 60: self.history.pop() # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过 # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败 if len(self.history) < 3: self.history.insert(0, ctime) return True else: return False def wait(self): import time ctime = time.time() return 60 - (ctime - self.history[-1])
视图函数使用代码
from app01.auth import MyThro class Book(APIView): authentication_classes = [myAuthen, ] permission_classes=[] throttle_classes=[MyThro,] def get(self, request): # request.user response = MyResponse() # request.GET print(request.user.name) print(request.auth.token) # 必须登陆才能访问 books = models.Book.objects.all() ret = myserial.BookSer(instance=books, many=True) response.msg = '查询成功' response.data = ret.data return JsonResponse(response.get_dic, safe=False)
2、使用
频率: -定义一个类: from rest_framework.throttling import SimpleRateThrottle class MyThro(SimpleRateThrottle): scope = 'aaa' def get_cache_key(self, request, view): # return self.get_ident(request) return request.META.get('REMOTE_ADDR') -setting中配置 "DEFAULT_THROTTLE_RATES":{ 'aaa':'3/msssssss' } -全局使用: 'DEFAULT_THROTTLE_CLASSES':['app01.auth.MyThro',], -局部使用: throttle_classes=[MyThro,]
eg.
from rest_framework.throttling import SimpleRateThrottle class MyThro(SimpleRateThrottle): scope = 'aaa' def get_cache_key(self, request, view): # return self.get_ident(request) return request.META.get('REMOTE_ADDR') # return
3、假如全局限制每分钟查看3次,视图函数每分钟10次,则需要在
"DEFAULT_THROTTLE_RATES":{
'aaa':'3/msssssss'
},
from rest_framework.throttling import SimpleRateThrottle
class MyThro(SimpleRateThrottle):
scope = 'aaa'
def get_cache_key(self, request, view):
# return self.get_ident(request)
return request.META.get('REMOTE_ADDR')
# return
将aaa 赋值为10/mmmmms