RESTful规范(二)

七 解析器

解析器的作用:

-用来解析前台传过来的数据编码方式
   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

原文地址:https://www.cnblogs.com/di2wu/p/10141118.html