- rest_framework/views.py/APIView.dispatch()
- 对传入的request参数进行扩展
def dispatch(self, request, *args, **kwargs):
""" `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """
""" 扩展了django原生的dispatch()方法 """
self.args = args
self.kwargs = kwargs
# 扩展reqeust
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
# 这里和原生的dispatch()基本同样
# 重写了initial()方法
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
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)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
复制代码
def initialize_request(self, request, *args, **kwargs):
""" Returns the initial request object. """
parser_context = self.get_parser_context(request)
# 增长了authenticators等成员
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
复制代码
def get_authenticators(self):
""" Instantiates and returns the list of authenticators that this view can use. """
# 列表生成式,生成了相应的类的对象的列表:[对象,对象,...]
return [auth() for auth in self.authentication_classes]
class APIView(View):
# The following policies may be set at either globally, or per-view.
# 下面的策略 可能在setting中设置,或者重写在每一个View中
...
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
...
复制代码
def initial(self, request, *args, **kwargs):
""" Runs anything that needs to occur prior to calling the method handler. """
...
# Ensure that the incoming request is permitted
# 执行了认证、鉴权、限流这三个方法
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
复制代码
接上回,restframework重写的dispatch()方法中,执行了inital()函数。其中perform_authentication(request) 方法实现了请求的认证功能。redis
def perform_authentication(self, request):
""" Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either `request.user` or `request.auth` is accessed. """
# 执行新封装的request对象的的user方法(是个property因此不用user() )
request.user
复制代码
@property
def user(self):
""" Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """
if not hasattr(self, '_user'):
with wrap_attributeerrors():
# 通过一些判断以后,跳转到_authenticate()方法
self._authenticate()
return self._user
复制代码
def _authenticate(self):
""" Attempt to authenticate the request using each authentication instance in turn. """
""" 尝试去执行每个认证对象实例中的authenticate()方法,返回认证结果 """
# 获取每个认证对象实例
for authenticator in self.authenticators:
try:
# 使用认证对象的authenticate()方法
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
# 若是认证对象没有写authenticate()方法,抛出异常_not_authenticated()
self._not_authenticated()
raise
# 若是写了authenticate()方法,而且执行后返回的不是None
# 则给request对象实例生成3个成员 self._authenticator, self.user, self.auth
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
# 若是执行authenticate()方法以后返回的是None
# 则继续循环,执行对象列表中下一个 认证对象的方法,直到最后一个对象
self._not_authenticated()
复制代码
def authenticate(self, request):
return (self.force_user, self.force_token)
复制代码
from rest_framework.authentication import BaseAuthentication,exceptions
class MyAuthentication(BaseAuthentication):
# 必需要重写authenticate()、authenticate_header()方法
def authenticate(self, request):
# 重新的request中若是找不到成员,会继续从原生_request中查找
token = request.GET.get('token')
# 检查token,实际应该与数据库中token比对,这里简写
if token and token=='123456abc':
# 返回元祖(user, token)
return ('clay', token)
else:
# 若是没有token或者token错误,抛出认证失败的异常
raise exceptions.AuthenticationFailed('用户认证失败')
def authenticate_header(self, request):
pass
复制代码
class AssetsView(APIView):
# 重点看这一行,重写authentication_classes列表,调用认证类
# 自定义的类MyAuthentication此时是最后一个认证类,因此必须返回(user, token)
authentication_classes = [MyAuthentication, ]
def get(self,request,*args,**kwargs):
""" 返回带code、msg的assets json数据 :param requset: :param args: :param kwargs: :return: """
# 提取数据库中assets信息, 整合成字典(先使用土法1)
asset_list = []
for asset in models.Assets.objects.all():
asset_dict = {
'id': asset.id,
'name': asset.name,
}
asset_list.append(asset_dict)
ret = [
{
'code': 1000,
'msg': 'SUCCESS: get assets info summary',
# 调用认证成功返回的元祖
'user': request.user,
'token': request.auth,
}
] + asset_list
# 返回数据库中所有assets概览
# 返回带状态码,成功响应默认就是200,能够修改
return HttpResponse(json.dumps(ret), status=200)
复制代码
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ['restframework_class.auth.MyAuthentication', 'restframework_class.auth.MyAuthentication2']
}
复制代码
restframework重写的dispatch()方法中,执行了inital()函数。inital()中check_permissions(request) 方法实现了请求的鉴权、权限控制功能。数据库
def check_permissions(self, request):
""" Check if the request should be permitted. Raises an appropriate exception if the request is not permitted. 检查请求是否被权限限制。 若是请求没有权限,抛出对应的异常 """
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
复制代码
def get_permissions(self):
""" Instantiates and returns the list of permissions that this view requires. 列表生产式,来生成权限类的列表 """
return [permission() for permission in self.permission_classes]
复制代码
def permission_denied(self, request, message=None):
""" If request is not permitted, determine what kind of exception to raise. """
if request.authenticators and not request.successful_authenticator:
raise exceptions.NotAuthenticated()
# 抛出PermissionDenied(detail=message)异常,出入权限对象的message变量
raise exceptions.PermissionDenied(detail=message)
复制代码
class BasePermission(object):
""" A base class from which all permission classes should inherit. """
def has_permission(self, request, view):
""" Return `True` if permission is granted, `False` otherwise. """
return True
def has_object_permission(self, request, view, obj):
""" Return `True` if permission is granted, `False` otherwise. """
return True
复制代码
from rest_framework.permissions import BasePermission
class NetPermission(BasePermission):
message = "必须是网络工程是才能访问!"
def has_permission(self, request, view):
if request.user.role != 0:
return False
return True
复制代码
class AssetsView(APIView):
authentication_classes = [MyAuthentication, ]
# 关注这一行
# 在View中重写permission_classes权限类列表,几个类表示要通过几层权限检查
permission_classes = [NetPermission, ]
def get(self,request,*args,**kwargs):
...
return HttpResponse(json.dumps(ret), status=200)
复制代码
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ['plugin.restframework.authentication.MyAuthentication', 'plugin.restframework.authentication.MyAuthentication2'] ,
"DEFAULT_PERMISSION_CLASSES": ['restframework.permissions.MyPermission', ]
}
复制代码
restframework重写的dispatch()方法中,执行了inital()函数。inital()中check_throttles((request) 方法实现了请求的访问频率控制功能。django
def check_throttles(self, request):
""" Check if request should be throttled. Raises an appropriate exception if the request is throttled. 检查请求是否应该被节流。 若是被节流,则抛出相应异常。 """
# 遍历 限流对象列表,若是返回Fasle,则被限流,抛出异常(可传参数throttle.wait()的返回值)
for throttle in self.get_throttles():
# 若是被节流,返回False,则抛出相应异常
if not throttle.allow_request(request, self):
self.throttled(request, throttle.wait())
复制代码
def get_throttles(self):
""" Instantiates and returns the list of throttles that this view uses. """
# 能够在view中重写throttle_classes,指定限流对象列表
# 也能够在setting.py中定义
return [throttle() for throttle in self.throttle_classes]
复制代码
def throttled(self, request, wait):
""" If request is throttled, determine what kind of exception to raise. """
# wait参数,出入的值是 节流类的wait()方法的返回值(单位:秒)
raise exceptions.Throttled(wait)
复制代码
from rest_framework.throttling import BaseThrottle
import time
# 访问记录,key是IP,value是访问时间的列表。应该记录在redis缓存中,这里简单写在内存里。
REQ_RECORD = {}
class MyThrottle(BaseThrottle):
""" 自定义限流类:10秒内只能访问3次,超过就限流 返回True,容许请求访问 返回False,禁止请求访问 """
# 经过 self.history这个对象的成员变量,
# 在allow_request()和 wait()这两个成员方法之间传递history的值
def __init__(self):
self.history = None
def allow_request(self, request, view):
remote_addr = request.META.get('REMOTE_ADDR')
timer = time.time()
if remote_addr not in REQ_RECORD:
REQ_RECORD[remote_addr]=[timer]
return True
history = REQ_RECORD[remote_addr]
self.history = history
# 若是历史访问时间列表的最老的访问时间 在10秒以前,
# 将最老的访问时间 从历史访问时间列表 中移除
while history and history[-1] < timer - 10:
history.pop()
# 10秒内的访问记录,是否超过3次
# 若是没有超过,则记录此次访问,并返回True,容许访问
# 若是超过,则返回False,禁止访问
if len(history) < 3:
history.insert(0, timer)
return True
return False
def wait(self):
timer = time.time()
return 10 - (timer - self.history[-1])
复制代码
class AssetsView(APIView):
authentication_classes = [MyAuthentication, ]
permission_classes = [NetPermission, ]
# 关注这一行
# 在View中重写throttle_classes限流类列表,通常只写一个限流,
# 或者不限流,使列表为空,throttle_classes = []
throttle_classes = [MyThrottle, ]
def get(self,request,*args,**kwargs):
...
return HttpResponse(json.dumps(ret), status=200)
复制代码
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ['plugin.restframework.authentication.MyAuthentication', 'plugin.restframework.authentication.MyAuthentication2'],
"DEFAULT_PERMISSION_CLASSES": ['restframework.permissions.MyPermission', ],
"DEFAULT_THROTTLE_CLASSES": ['plugin.restframework.throttling.MyThrottle']
}
复制代码
与认证类、鉴权类一般继承BaseXXX类,高度自定义不一样,限流类我我的以为继承restframework提供的其余内置的类更方便json
- 例如继承 SimpleRateThrottle 类
class ZklTrottle(SimpleRateThrottle):
# 设定规定时间内能访问的次数,例如 3/m, 1/s, 1000/h, 9999/day
# 一般设定在setting.py中
THROTTLE_RATES = {
"Zkl": '5/m'
}
# 指定scope值为 查找THROTTLE_RATES的key
scope = "Zkl"
# 定义标识一个用户的参数
# get_ident(request)是BaseThrottle类中的方法,返回remote_addr,
# 即便用访问源IP做为一个用户的标识
def get_cache_key(self, request, view):
return self.get_ident(request)
# return request.user.pk # 一般也使用requser.user做为标识一个用户的ID
复制代码