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用户的访问
    }
}
原文地址:https://www.cnblogs.com/k5210202/p/13082067.html