• django组件rest_framework (上)


    rest_framework

    rest_framework是一套基于Django的restful接口协议的组件,是一个强大灵活的构建 Web API 的工具包,它可以方便根据restful接口协议规则快速请求进行预处理,减少视图函数中的处理逻辑,实现快速开发。

    安装和使用

    # 安装
    pip install djangorestframework

    # 如果已经安装了低于2.2版本的django, 直接安装djangorestframework 将会自动卸载旧版本django而安装3.x版本,如果要使用django2.2版本可以先安装 django2.2 pip install django==2.2
    # 导入 import rest_framework

    普通的视图类

    from django.views.generic.base import View
    
    class OrderView(View):     # 继承自View
        def dispatch(request, *args, **kwargs):  # 根据请求做分发
            super().dispatch(request, *args, **kwargs)
    
        def get(self, request):
            pass
    
        def post(self, request):
            pass
    
        def put(self, request):
            pass
    
        def delete(self, request):
            pass

    restframework中的视图类APIView

    from rest_framework.views import APIView
    
    
    class OrderView(APIView):    # 不在继承View类而是rest_framework中的APIView,APIView同样继承自View
        def dispatch(request, *args, **kwargs):  # 根据请求做分发
            super().dispatch(request, *args, **kwargs)
        
        def get(self, request):
            pass
    
        def post(self, request):
            pass
           
        def put(self, request):
            pass
    
        def delete(self, request):
            pass

    继承自APIView,该类重新定义了dispatch方法,在视图类根据请求进行分发前,提前对请求进行了预处理,然后再分发到各个处理的视图函数中。查看APIView源码如下

    class APIView():
        def dispatch(self, request, *args, **kwargs):
            # 添加了一些功能
            # request._request原来的request对象
            # request.authenticators = [认证类对象],多个认证对象
            # 认证类对象从在self.authentication_classes列表中指定认证类,需要我们定义认证类,提供了基础类
            request = self.initialize_request(request, *args, **kwargs)  
            self.request = request
            
            try:
                self.initial(request, *args, **kwargs)
            # 内部self.perform_authentication(request)进行用户验证,验证使用requests上的验证类
            # self.check_permissions(request)检查权限
            # self.check_throttles(request)节流限制
            # self.determine_version(request, *args, **kwargs) 版本控制
    
                # 反射获取并调用对应的视图类的方法,完成视图函数的工作
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)  # 并直接调用了得到response
    
            except Exception as exc:
                response = self.handle_exception(exc)
                # 内部会调用exception_handler去处理这些错误
    
            # 得到的resopnse再次封装
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response

    从上面源码中,在APIView中的dispatch主要做了以下事情:

    • request = self.initialize_request(request, *args, **kwargs):中重新封装了一个新的request对象,将原来的request绑定到该对象上,并尝试绑定认证类对象(用户登录认证),
    • self.initial(request, *args, **kwargs):该方法中将会实现,用户登录,用户类型的权限控制,访问节流(单位时间访问次数控制),版本信息等。
    • try语句中各种异常被捕获后,都将被self.handle_exception(exc)处理,处理方式是根据错误类型,使用对应的状态和错误提示信息构造一个Response对象返回给前端即可。
    • self.response = self.finalize_response(request, response, *args, **kwargs):完善response的信息。

    新的Request对象 

    request = self.initialize_request(request, *args, **kwargs)将远request进行分装,得到一个新的request对象

    class Request:
        def __init__(self, request, parsers=None, authenticators=None,
                     negotiator=None, parser_context=None):
            assert isinstance(request, HttpRequest), (
                'The `request` argument must be an instance of '
                '`django.http.HttpRequest`, not `{}.{}`.'
                .format(request.__class__.__module__, request.__class__.__name__)
            )
    
            self._request = request                # 原request对象被封装到_request属性之中
            self.parsers = parsers or ()
            self.authenticators = authenticators or ()
            self.negotiator = negotiator or self._default_negotiator()
            self.parser_context = parser_context
            self._data = Empty                    
            self._files = Empty                    
            self._full_data = Empty               
            self._content_type = Empty
            self._stream = Empty

    四个组件

    四个组件的使用方式

    class OrderView(APIView):  
        authentication_classes = [MyAuthentication, ]       # 对 user 进行认证
        permission_classes = [MyPermission,]                # 对权限进行检查
        throttle_classes = [Mythrottle]   # 使用对应的限流类,权限等信息
        
        def get(self, request):
            pass
    
        def post(self, request):
            pass

    self.initial(request, *args, **kwargs)调用过程中,执行了进行了四个组件的执行过程

        def initial(self, request, *args, **kwargs):
            self.format_kwarg = self.get_format_suffix(**kwargs)
    
            version, scheme = self.determine_version(request, *args, **kwargs)    # 版本控制
            request.version, request.versioning_scheme = version, scheme
    
            # Ensure that the incoming request is permitted
            self.perform_authentication(request)             # 认证
            self.check_permissions(request)                  # 权限检查
            self.check_throttles(request)                    # 节流限制

    以上认证,权限,节流三个方法调用后,将会使用绑定到视图中的对应类对请求进行预处理

    认证

    认证函数内部将会调用request.user,通过绑定到视图类中的认证类对该user对象进行判断。该类需要实现authenticate方法

    from rest_framework.authentication import BaseAuthentication
    
    
    class MyAuthentication(BaseAuthentication):     # 可以继承也可以不继承,自己实现方法即可
        def authenticate(self, request):
            token = request._request.GET.get("token", None)
    
            if not token:
                raise exceptions.AuthenticationFailed("用户认证失败")  # 该错误会被捕获
    
            user = Auth.objects.filter(token=token).first()
            if not user:
                return None         # 返回None将会是匿名用户
            return (user, None)
            #  返回值将作为 request._user, request.auth = (user, None)

    认证类的实现需要根据request中的内容,查询数据库得到用户对象,如果有返回(user, None), 没有则报错,返回None则默认为使用匿名用户的方式继续访问。

    权限

    from rest_framework.permissions import BasePermission
    
    class NormalPermission(BasePermission):
        message = "当前用户的权限为%s:权限不够"
        level = 1    # 假设1为基本权限
        def has_permission(self, request, view):
            permission = request._user.permission
    
            if permission > self.level:
                return True
            # 实例定义一个message属性,报错会提示给前端
            self.message = self.message % str(permission)
            return False

    应该在has_permission方法中检查request._user的权限,判断是否满足该类的权限要求,满足返回True,否则返回False权限不足。可以定义message的提示信息,权限不足时会报错,这时候会将这个message作为response信息发送给前端。

    节流

    登录用户使用用户id作为一个key,在列表中记录该key的访问记录,0号元素为上一次访问,-1号元素为最远的一次访问。并指定访问频率例如 "25/m": 每分钟25次

    当一次请求到来时,通过该用户的key找到访问记录列表,依次pop()掉所有超过一分钟的记录,剩下的全是1分钟内的记录,如果列表长度大于设定值(上面为25),表示访问过于频繁。将返回False表示拒绝访问,列表未满表示还可以接受访问。

    通常我们只需要使用内部定义类即可完成节流,通过继承SimpleRateThrottle类,并定义get_cache_key(self, request, view)方法,返回一个与用户一一对应key,用于区分个个用户访问。

    # 匿名用户限制
    class AnyoneThrottle(SimpleThrottle):
        scope = "anyone"
        def get_cache_key(self, request, view):
            return self.get_ident(request)      # 调用Base中的get_ident方法,获取ip地址作为key
    
    # 登录用户限制
    class UserThrottle(SimpleThrottle):
        scope = "user"
        def get_cache_key(self, request, view):
            return self.request.user.username   # 通过用户名作为key 
    
    class VIPThrottle(SimpleThrottle):
        scope = "VIP"
        def get_cache_key(self, request, view):
            return self.request.user.username   # 通过用户名作为key 
    scope 属性指定的名字用于从配置文件中获得该类处理节流的对策配置。
    REST_FRAMEWORK = {"DEFAULT_THROTTLE_RATES": {
            "anynoe": "5/m",        # 匿名用户的访问
            "user": "20/m",         # 登录用户的访问
            "VIP": "40/m"           # vip用户的访问
        }
    }

    如果需要自定义节流类,可以继承Basethrottle类,并实现allow_request方法即可:返回True表示可以继续访问,返回False表示拒绝,拒绝的同时需要在wait()方法中返回一个下一次访问的等待时间,用于返回给前端使用

    全局配置

    如果视图类中没有配置authentication_classes,permission_classes,throttle_classes属性,程序将默认从配置文件中读取全局默认配置,如果再类中已配置则优先使用自己的。全局配置文件的内容如下。

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": ['API.utils.auth.Authentication', ],
        # 其中写认证的类的路径,不要在views中,通常在utils目录下auth.py中定义自己的认证类
        "UNAUTHENTICATED_USER": None,      # request.user = None
        "UNAUTHENTICATED_TOKEN": None,     # request.auth = None
        # 匿名token,只需要函数或类的对应的返回值,对应request.auth=None
        "DEFAULT_PERMISSION_CLASSES": [],
        "DEFAULT_THROTTLE_CLASSES": [],
        "DEFAULT_THROTTLE_RATES": {
            "anynoe": "5/m",        # 匿名用户的访问
            "user": "20/m",       # 登录用户的访问
            "VIP": "40/m"        # vip用户的访问
        }
    }
  • 相关阅读:
    基于 HTML5 Canvas 的交互式地铁线路图
    基于HTML5的WebGL实现json和echarts图表展现在同一个界面
    基于HTML5和WebGL的3D网络拓扑结构图
    JavaScript基础:创建对象
    使用ksoap2报java.io.EOFException异常问题解决方法
    Silverlight之我见
    今年的IT大趋势是虚拟现实
    Apache服务器部署ASP.NET网站
    Bootstrap优秀网站:乐窝网
    [转载]C#读取Excel几种方法的体会
  • 原文地址:https://www.cnblogs.com/k5210202/p/13082067.html
Copyright © 2020-2023  润新知