Django框架rest_framework中APIView的as_view()源码解析、认证、权限、频率控制
在前后端分离项目中前面我们也提到了各种认证需要自己来做,那么我们用rest_framework的时候
rest_framework也为我们提供相应的接口,rest_framework中的APIView实现了和Django原生View as_view()一样的功能
并在此基础上实现了原生request的封装、认证、权限、视图等等功能
我们来看APIView的as_view如何实现的:
通过上篇对View源码的分析我们可以得知,在View的的闭包函数view中调用了dispatch方法,那么我们在找dispatch的时候还是要从self开始找,
此时的self是我们在视图层定义的视图类的对象,视图类并没有定义dispatch,那么就找父类APIView,在APIView中我们找到了dispatch
那么意味着APIView对dispatch进行了重写,我们来看看APIView怎么封装的dispatch方法:
我们继续深入initialize_request()去看看它是怎么封装原生request的:
到这里,我们可以知道,它将原生的request和认证等组件给到Request类实例化返回,我们仍然需要进一步去看Request的源码:
这里我们可以得知,它将原生的request封装到了新的request对象的_request属性中,那么你就会想了,那原生request 的数据和方法都到哪里去了呢?
别着急,它也给你做了封装,继续看:
GET请求的数据:
它将原生GET请求的数据放到了query_parms里面,我们在视图类中通过request.query_parms就可以取到
POST请求数据:
这里的data不仅仅是POST的数据,所有请求方式的键值对数据的都会被放入data里面,支持urlencoded、form-data、json(application/json)
FILES数据:
对USER的封装:
此外还封装了auth等其他功能,这些功能不仅在新的request里面有,它也同样存在原生的request里,在该方法的解释上,它讲明了它支持Django底层contrib,并将user设置在了Django原生的HttpRequest实例中,以保证在任何Django原生中间件都可以通过校验,所以我们在使用APIView时可以几乎不用考虑兼容性问题
好了,我们刚刚看完了initialize_request()源码,了解了APIView对原生request进行如何进行封装之后
接下来我们来看initial是如何帮我们做认证、权限等校验的
认证校验
首先我们来看认证校验:
按住Ctrl点进去之后发现它就一行代码
request.user
那此时我们继续找Request类中的user,
发现它执行了_authenticate(),然后由于好奇心,咱继续往下点:
通过上图的分析我们得知,self.authenticators 这个里面装着一个个的认证类对象,那么这些认证类对象是哪里来的呢?我们继续探究:
我们回到Request封装原生request实例化的地方看传入的是什么东西
我们继续找 get_authenticators()方法!
发现是从self.authentication_classes中拿到的,果然返回的是一个装有对象的列表
我们继续找authentication_classes
我们在视图类中没有定义authentication_classes属性,那么继续往上找发现:
这时候看到它是从api_settings里找的,我们或多或少应该明白了,它会从用户配置中去找这个属性,但是我们并没有早配置文件中配置这个属性,也就意味self.get_authenticators()拿到的是空列表,刚才所有的流程都没有走,意味着APIView没有任何的认证措施,那么这些认证措施就需要我们自己来做了。。。。
如何自定义认证类?
怎么做呢?缺什么补什么,在找authentication_classes的时候它会先去我们的视图类中找,那么我们就在视图类中配置这么个属性,刚才也推导过了,它是个列表或者元祖,那么我们就用列表好了,进一步反推,它会for循环这个列表并拿出一个个类.authenticate(),那么我们就先写一个类并实现authenticate方法,将类名放到该列表中,刚才我们推理得出,authenticate方法可以没有返回值,也可以返回两个值,一个是user对象,一个是auth对象,如果有返回值,它将user对象赋值给了request.user,这时候我们明白了,它的内部是在认证完了后将用户对象塞给了request,此时的request就拥有了user这个全局属性
我顺便去搂了一眼官方文档,我们需要自己定义认证类并且继承BaseAuthentication,那么接下来我们认证的写代码:
那么至此,我们在用APIView的时候自定义认证就做完了,authenticate中的认证逻辑是可以根据自身公司和项目需要去做的,这里是给出最简单的示范。
当然,如果刚才你的思路一直走过来你会发现,我们除了在视图类中配置authentication_classes以外还可以在配置文件中配置,配置方法如下:
REST_FRAMEWORK={"DEFAULT_AUTHENTICATION_CLASSES":["app01.views.MyAuth",]}
如果这样配置了的话,在全局的视图类都会生效,显然对于网站注册登陆主页进行校验用户认证是不合理的,那我们需要怎么做呢
由于源码中查找authentication_classes的顺序是先从视图类找,视图类没有然后找配置文件,那么我们可以在不需要校验的视图类中定义
authentication_classes = []
在相应视图类中将它配置为空列表即可。
下面我们来总结下用法:
1、定义认证类(项目中一般在应用里单开py文件存放) 2、在认证类中实现authenticate方法,实现具体的认证逻辑 3、认证通过返回user_obj和认证对象,这样request就拥有了全局的user,认证不给前端抛异常4、配置-在视图类中配置-在全局配置,局部禁用
刚才说了APIView除了有认证,还有权限和频率控制
权限校验
权限校验的源码和认证几乎一模一样的思路,用法一模一样
也是需要自定义权限类,实现has_perission()方法,代码如下:
from rest_framework.permissions import BasePermission # 导入BasePermissionclass MyPermision(BasePermission): # 继承BasePermissionmessage = '不是超级用户,查看不了' # 自定义返回给前端的访问错误信息def has_permission(self,request,view):if request.user.user_type==1:return Trueelse:return Falseclass Book(APIView): permission_classes = [MyPermision,] # 视图类配置def get(self,request):pass
也可以全局配置,局部禁用:
在配置文件REST_FRAMEWORK中加上配置
REST_FRAMEWORK = {"DEFAULT_AUTHENTICATION_CLASSES":['app01.views.MyAuth',],"DEFAULT_PERMISSION_CLASSES" : ['app01.views.MyPermission',] ## ---加上这一行即可 }
局部禁用方式一样是在视图类中配置
permission_classes = []
频率控制
-使用:-第一步,写一个频率类,继承SimpleRateThrottlefrom rest_framework.throttling import SimpleRateThrottle#重写get_cache_key,返回self.get_ident(request)#一定要记住配置一个scop=字符串class MyThrottle(SimpleRateThrottle):scope = 'lxx' def get_cache_key(self, request, view):return self.get_ident(request) # 这里是频率控制的条件,是以访问IP来控制还是以用户ID控制都可以,暴露的配置接口,#这里调用的父类get_ident f方法-第二步:在setting中配置REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES':{'lxx':'3/m' # 这里的lxx 即在自定义频率类中定义的scope }}-局部使用-在视图类中配置:throttle_classes=[MyThrottle,]-全局使用-在setting中配置 'DEFAULT_THROTTLE_CLASSES':['自己定义的频率类'],-局部禁用throttle_classes=[]
我们来看源码:
还是从initial()这里进
点进去看
这段是频率校验的核心代码,self.get_throttles()走的是和上面认证、权限一样的路子
也是去自定义频率类、然后配置文件里找相应频率控制类并且直接加()实例化了
现在每次for循环出来的 throttle 都是一个实例化的对象,
if not throttle.allow_request(request, self):
allow_request()从字面意思上我们也能判断出它应该是校验是否对本次访问放行的,那么它的返回值应该就是True或者False
那么这句判断语句的意思就是:如果频率校验不通过,那么就走if块内部代码
self.throttled(request, throttle.wait())
这句代码的意思就是针对 throttle.wait()得到的不同限制结果抛出不同异常给前端用户
点进去看源代码人家也是这么干的
def throttled(self, request, wait):"""If request is throttled, determine what kind of exception to raise."""raise exceptions.Throttled(wait)
那么频率校验不通过的情况我们知道了,我们就要关注它内部是如何实现对频率的校验的
进到allow_request里面去看,由于我们自定义的频率校验类没有定义该方法,那么就向它的父类找
我们来看源码人家是怎么做的
# settings配置 """REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES':{'lxx':'3/m' # 频率配置 每分钟3次}} """ #以下源码+个人注释def allow_request(self, request, view):if self.rate is None: # self.rate 是 get_rate() 通过我们声明的scope = 'lxx' 去拿到的配置文件中频率配置中频率值 '3/m' ,# 需要我们在频率控制类中声明scope = 'lxx',也就是配置中字典的键,# 键是通过反射scope拿到的,详见 get_rate()源码return Trueself.key = self.get_cache_key(request, view) # self是自定义频率类的对象,那么get_cache_key将优先从自定义频率类找# 得到的是频率控制的依据,是用户IP\ID或者其他信息,由重写get_cache_key确定if self.key is None:return Trueself.history = self.cache.get(self.key, []) # 从缓存中拿到当前用户的访问记录self.now = self.timer() # 获取当前时间戳#self.now 是当前执行时间,self.duration是访问频率分母,以上述例子为例 这里就是60while self.history and self.history[-1] <= self.now - self.duration:self.history.pop() # 将访问列表超时的去掉 history =[第三次访问时间,第二次访问时间,第一次访问时间]# self.num_requests 访问频率分子,设置 '3/m' 表示每分钟3次,以上例子为例,这里就是 3if len(self.history) >= self.num_requests:return self.throttle_failure() # 返回访问失败信息return self.throttle_success() # 访问成功
以上的self.duration 和self.num_requests在访问时自定义频率控制类实例化的时候,已经通过父类的__init__产生,源码如下:
这里又进一步调用了self.parse_rate()方法,传入了自定义频率类中的scope属性值 :'3/m', 那么这里我们大概就能猜到parse_rate()
干了件什么事儿,对!那就是拆分这个字符串,并返回 限制次数,限制时间长度,比如 3次,60秒
一起来看parse_rate()源码:
def parse_rate(self, rate):"""Given the request rate string, return a two tuple of:<allowed number of requests>, <period of time in seconds>"""if rate is None:return (None, None)# 对 '3/m' 进行切分 num_requests=3,duration = 60num, period = rate.split('/')num_requests = int(num)duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] # period[0]取到切分后字符串 'm' 的第一个元素return (num_requests, duration) # (3, 60)
以上allow_request()如果校验不通过,那么直接调用 throttle.failure()直接返回false
如果校验通过,它应该还需要将本次访问添加到访问记录中并保存了,那么我们猜 throttle.success() 干了这么件事
来看源码发现果然干了这么件事儿!
def throttle_success(self):"""Inserts the current request's timestamp along with the keyinto the cache."""self.history.insert(0, self.now) # 将当前访问时间插入 访问记录第一个位置self.cache.set(self.key, self.history, self.duration) # 更新访问信息记录缓存return True # 返回访问成功 True 供判断
转载于:https://www.cnblogs.com/gwklan/p/11140393.html
Django框架rest_framework中APIView的as_view()源码解析、认证、权限、频率控制相关推荐
- rest_framework-00-规范-APIview源码解析-认证
rest_framework-00-规范-APIview源码解析-认证 规范 支付宝: 接口开发 订单api----order 方式1:缺点:如果有10张表,则需要40个url. urls.py vi ...
- java if中的continue_java中break和continue源码解析
在自己学习java语言的过程中,很容易把break和continue的用法混淆.为了便于以后快速查阅及温习,在此特留学习笔记一份. 简述 在任何迭代语句的主体部分,都可以用break和continue ...
- underscoreJs中pluck函数的源码解析
9月份之后项目开始进入收尾期了,产品要上市,所以9月之后的两个月都在疯狂的改BUG.最近总算是基本结束了,只剩下扫尾的了.终于能静下心来好好研究技术了.最近遇到两个函数,分别是underscore中的 ...
- HBase中MemStore flush的源码解析
flush请求的发出: HRegion会调用requestFlush()触发flush行为,flush发生在每一处region可能发生变化的地方,包括region有新数据写入,客户端调用了put/in ...
- php的lumen框架,Lumen框架“服务容器”源码解析
1.服务容器 "服务容器"是Lumen框架整个系统功能调度配置的核心,它提供了整个框架运行过程中的一系列服务."服务容器"就是提供服务(服务可以理解为系统运行中 ...
- [源码解析] 深度学习分布式训练框架 horovod (11) --- on spark --- GLOO 方案
[源码解析] 深度学习分布式训练框架 horovod (11) - on spark - GLOO 方案 文章目录 [源码解析] 深度学习分布式训练框架 horovod (11) --- on spa ...
- [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark
[源码解析] 深度学习分布式训练框架 horovod (10) - run on spark 文章目录 [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark ...
- 隔一段时间撸一次,特别香,HashMap中remove、getOrDefault源码,一遍一遍、又一遍
前言 点赞在看,养成习惯. 点赞收藏,人生辉煌. HashMap系列文章 第一篇 HashMap源码中的成员变量你还不懂? 来来来!!!整理好的成员变量源码解析 第二篇 撸啊撸,再次撸HashMap源 ...
- AsyncTask源码解析,你需要摸清的细节
AsyncTask简介 1. AsyncTask提供了一种恰当的简单的跟UI Thread交互的方式. 2. 它不需要通过操控Threads或Handler就可以将后台操作以及其结果展示在UI Thr ...
- Dubbo的可扩展机制SPI源码解析
内容概要: Dubbo SPI案例演示 Dubbo SPI主流程源码解析 Dubbo中的依赖注入源码解析 Dubbo中的AOP实现源码解析 Dubbo中的Adaptive机制源码解析 文章目录 一.D ...
最新文章
- 如何完全安装mysql数据库_数据库经验:如何简单安装MySQL数据库
- MVP模式在Android中的应用之图片展示选择功能的框架设计
- QMouseEvent
- 高并发,高性能的一点调研
- Spring MVC入门示例教程--表单处理
- bzoj4817: [Sdoi2017]树点涂色
- centos 账号安全设置
- [设计模式] ------ 策略模式
- 计算机网络与维护考试题,《网络管理与维护》试题库.doc
- C语言读取load格式文件,求指导,如何用c语言实现读取*.raw格式图像
- JMW-Label标签设计打印源码
- po 价格条件表_海纳易拓图文讲解SAP MM模块采购价格条件
- 14的虚拟机可以用在15上面吗_【Linux虚拟机】在Windows上安装Linux虚拟机
- 深圳大数据学习:怎样进行大数据的入门级学习?
- 百度网盘文件转存到阿里云盘工具,爱死这个软件了
- torrents.php怎么下载,PT站自动收藏免费种下载|PT Add Free Torrents To Bookmark脚本js插件_ - 极光下载站...
- 用python画蜡笔小新的步骤_蜡笔小新 - python代码库 - 云代码
- lzg_ad:FBWF技术概述
- ArcGIS 线简化算法的使用及两种方法的比较
- Excel 调用百度翻译API进行翻译