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用户的访问 } }