1
drf 源码
# CBV 类型的全是根据反射来的,基于反射实现根据请求方式不同,执行不同的方法
# 原理 :
# a.
url - view() - dispath
class StudentsView(View):
def dispatch(self, request, *args, **kwargs):
func = getattr(self, request.method.lower())
ret = func(request, *args, **kwargs) # 各个函数执行,以及参数 get()....
return ret
def get(self,request):
return HttpResponse('GET')
cbv的运行流程
def dispatch(self, request, *args, **kwargs):
ret = super(StudentsView, self).dispatch(request, *args, **kwargs)
return ret
继承: (多个类公用的功能,为了避免重复)
面试题
-
django中间件
- 中间件最多可以写几个方法:5个
- process_request view response exception
- render_template
-
执行流程
-
中间件做过什么?
权限
用户登陆验证
django csrf_token 怎么实现的?(面试题)
在view里面 : request view 都是在请求前
-
from django.views.decorators.csrf import csrf_exempt
-
csrf_excmpt免除csrf请求
-
process_view方法
检查是否被@csrf_exempt(免除csrf认证)
去请求体或cookie重获取token
为什么是view,view之后才是请求执行,才能装饰器
-
CBV小知识点:
csrf时加到单独方法无效
class:...
# @csrf_exempt # 这么加不行
# @method_decorator(csrf_exempt)
def post(self, request):
return HttpResponse('POST')
必须加到dispath里面
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
func = getattr(self, request.method.lower())
ret = func(request, *args, **kwargs) # 各个函数执行,以及参数 get()....
return ret
或者类上
@method_decorator(csrf_exempt, name='dispatch')
class StudentsView(View):
这样写可以少些一个 distpatch 方法
总结:
- 本质 : 基于反射来实现
- 流程 : 路由 view dispatch (反射)
- csrf_token取消 不从中间件, 要加到类上或者dispatch csrf_exempt
- 扩展
- csrf
- 基于中间件的proces_view
- 装饰器给单独函数设置或者不适用
- csrf
restful 规范 (建议)
分类记
1.rest规范
请求是一类:
使用HTTPs协议 域名 版本 资源(名词) 过滤
请求头: method
响应:
状态码 错误处理 结果集 Hypermedia API
开发前后端分离
a :
用户管理 http://www.oldboyedu.com/add_user/ ....
返回值很多,返回值的时候,不好处理了
{
code:666,
}
b:
vue
$.ajax({})
#不好, 因为10张表 处理 ,就40条url了
# 规范 : 10个url , 用method来处理不同的
基于fbv写
# 1 根据method不同进行不同的处理
def order(request):
if request.method == "GET":
return HttpResponse('获取')
if request.method == "add":
return HttpResponse('获取')
if request.method == "del":
return HttpResponse('获取')
if request.method == "update":
return HttpResponse('获取')
基于cbv写
class orderView(View):
def get(self,request,*args,**kwargs):
return HttpResponse('获取')
def post(self,request,*args,**kwargs): # 创建
return HttpResponse('获取')
def put(self,request,*args,**kwargs): # 更新
return HttpResponse('获取')
def delete(self,request,*args,**kwargs):
return HttpResponse('获取')
RESTful 规范
-
API与用户的通信协议,总是使用HTTPs协议
-
域名上区分(解决跨域问题):
https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
www.luffycity.com
子域名上区分 api.luffycity.co
-
版本(就像bootstrap2/3/4,都有用)
url方式:
www.luffycity.com
www.luffycity.com/api
版本方式
www.luffycity.com/api/v1/# -
路径(面向资源编程)
视网络上任何东西都是资源,均使用名词表示(可复数)
-
method
- GET :从服务器取出资源(一项或多项)
- POST :在服务器新建一个资源
- PUT :在服务器更新资源(客户端提供改变后的完整资源)
- PATCH :在服务器更新资源(客户端提供改变的属性)
- DELETE :从服务器删除资源
-
过滤
通过在url上传参的形式传递搜索条件
- https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
- https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
- https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
- https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
-
状态码
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
-
错误处理
状态码是4xx时,应返回错误信息,error当做key。
{ ``error: ``"Invalid API key" }
-
返回结果
针对不同操作,服务器向用户返回的结果应该符合以下规范。
GET ``/``collection:返回资源对象的列表(数组) GET ``/``collection``/``resource:返回单个资源对象 POST ``/``collection:返回新生成的资源对象 PUT ``/``collection``/``resource:返回完整的资源对象 PATCH ``/``collection``/``resource:返回完整的资源对象 DELETE ``/``collection``/``resource:返回一个空文档
-
Hypermedia API
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
{``"link"``: { ``"rel"``: ``"collection https://www.example.com/zoos"``, ``"href"``: ``"https://api.example.com/zoos"``, ``"title"``: ``"List of zoos"``, ``"type"``: ``"application/vnd.yourformat+json" }}
以上可以不遵循, 视情况而定
restful api 规范都有什么? 谈谈你对他的认知
伪装成一个老手,讲故事
在使用他的时候,有些适应,有些不用 ,出现问题了,跨域了,
解决: cors
jsonp ..
在聊天, 不好,然后用了那个
2.djangorestframework 框架
pip3 install djangorestframework
带token可以执行操作
from rest_framework.views import APIView
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
# 登录认证基于这个类实现
# dispatch->reuqest(封装)->认证(initial)->per():request.user->user->获取认证对象-->类对象--->触发异常或者是token(正常) # 写了一个My的类,写了一个auth_calssess , 有的话就找本类的,没有就去父类里找
class Myauthentication(object):
def authenticate(self,request):
token = request._request.GET.get('token')
if not token:
raise exceptions.AuthenticationFailed('用户认证失败')
return ('alex',None)
def authenticate_header(self,val):
pass
class DogView(APIView):
# authentication_classes = [BaseAuthentication, ] # 获取的是类的对象,
authentication_classes = [Myauthentication, ] # 获取的是类的对象,
# self.dispatch
def get(self, request, *args, **kwargs):
ret = {
'code': 1000,
'msg': 'xxx'
}
return HttpResponse(json.dumps(ret), status=201)
def post(self,request,*args,**kwargs):
return HttpResponse('创建dog')
def put(self,request,*args,**kwargs):
return HttpResponse('更新dog')
def delete(self,request,*args,**kwargs):
return HttpResponse('删除dog')
基于这个类做的认真
dispatch
一.认证
a.认证
源码流程搞明白:搞不明白,加注释,入口
dispatch, 不仅会用,还知道原理
s7129
今日内容: 1 认证 2 权限 3 节流(访问频率控制) 4 版本
用到用户认证,需要先写一个类,然后,在写一个那个对象[,]
也可以全局用到
b.基本使用认证组件
- 解决:
- a.创建两张表
- b.用户登陆(返回token并保存到数据库)
c. 源码
authentication_classes = [Authtication,]
# self.dispatch 入口
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 对原生的request进行加工(追加)
# Request(request,parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context)
# request(原生request, [BaseAuthentication对象,], )
# 获取原生request, self._request
# 获取认证类的对象, request.authenticator
# 1.封装Request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# 2. 认证成功,走反射
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)
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
def get_authenticators(self):
# self.authentication_classes = [foo,bar]
return [auth() for auth in self.authentication_classes]
#对象,然后,自己写了这个类,和
如下:
from rest_framework.authentication import BaseAuthentication
# 重写这两个方法
def authenticate(self, request):
def authenticate_header(self, request): # pass就行
authentication_classes = [Authtication,]
# 用了drf , 对于重用的功能
class Authtication(object):
def authenticate(self,request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
# 在rest_framework内部会将整个两个字段赋值给request,以供后续操作使用
return (token_obj.user, token_obj)
def authenticate_header(self, request):
pass
def initial(self, request, *args, **kwargs):
# 4.实现认证
self.perform_authentication(request)
def perform_authentication(self, request):
request.user
request.user在 Request.py里
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
# 获取认证对象, 进行一步步的认证
self._authenticate()
return self._user
[BaseAuthentication对象,]
def _authenticate(self):
# [BaseAuthentication对象,]
# 循环认证类的所有对象
for authenticator in self.authenticators:
try:
# 执行认证类的authenticate方法
# 1.如果auth方法抛出异常,self._not_auth()执行
# 2.有返回值,必须得是元祖(request.user, request.auth)
# 3.返回None , 当前不处理,下一个认证来处理
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
def _not_authenticated(self):
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() # AnonymousUser
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() # None
else:
self.auth = None
执行了函数,函数就返回了
return (token_obj.user, token_obj) ,根据这里写的异常和元祖返回
d.源码配置 p=19
把写的类,返回元祖的认证的,返回异常的,写到setting里
然后所有的类默认都加了,不用在一个个单独写了
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.FirstAuthtication', 'api.utils.auth.Authtication' ]
}
全局都能用,局部不要用
class UserInfoView(APIView):
"""订单相关业务"""
# 类似于中间件, 装饰器的处理restframework的方法,认证登录方法
authentication_classes = []
def get(self, request, *args, **kwargs):
return HttpResponse('用户信息')
http://127.0.0.1:8000/api/v1/order/?token=851c293fe784dea90e079aad5d498f42
{
"code": 1000,
"msg": null,
"data": {
"1": {
"name": "xifu",
"age": 18,
"gender": "男",
"content": "..."
},
"2": {
"name": "xifu2",
"age": 18,
"gender": "男",
"content": "..."
}
}
}
http://127.0.0.1:8000/api/v1/order/
用户未登录
在最后 , _not_authenticated函数中
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() # AnonymousUser
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() # None
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
def reload_api_settings(*args, **kwargs):
setting = kwargs['setting']
if setting == 'REST_FRAMEWORK':
api_settings.reload()
所以在settings里全局使用设置, USER , 和 TOKEN
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
REST_FRAMEWORK = {
# 全局使用的认证类
# 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', 'app01.utils.auth.Authtication' ],
'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', ],
"UNAUTHENTICATED_USER": None, # 匿名用户, request.user= None
# 'UNAUTHENTICATED_TOKEN':None , # request.auth = None
}
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
在rest_framework中settings里
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
def reload_api_settings(*args, **kwargs):
setting = kwargs['setting']
if setting == 'REST_FRAMEWORK':
api_settings.reload()
匿名是request.user = None
不是模态对话框
浏览器提供的,填完之后, 加密
基于django来实现的
不会用这些的,都是自定义来实现的
e.内置认证类
-
认证类, 必须继承:from rest_framework.authentication import BaseAuthentication
-
其他认证类:
梳理:
-
使用
-
创建类:继承BaseAuthentication 实现auth..方法
-
返回值:
- None,下一个认证来执行
- 抛异常 exceptions.AuthenticationFailed('用户认证失败') #from rest_framework import exceptions
- (元素1,元素2) #元素1赋值给request.user;元素2赋值给request.auth
-
局部使用
-
class Authtication(object): def authenticate(self,request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用户认证失败') # 在rest_framework内部会将整个两个字段赋值给request,以供后续操作使用 return (token_obj.user, token_obj) def authenticate_header(self, request): pass
-
全局使用
-
REST_FRAMEWORK = { # 全局使用的认证类 # 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', 'app01.utils.auth.Authtication' ], 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', ], "UNAUTHENTICATED_USER": None, # (没登录) 匿名用户, request.user= None # 返回的是函数()所以写匿名函数 # 'UNAUTHENTICATED_TOKEN':None , # request.auth = None }
-
2.源码流程
先走dispatch,request封装,走initial,找所有的对象,执行方法(两个)可以在setting中设置,但是api.settings为什么和settings相关联,不太懂
-
dispatch
-
封装request
获取定义的认证类(全局/局部),通过列表生成式创建对象
-
-
initial
- perform_authentication
- request.user(内部循环...)
- perform_authentication
后面的东西和这里的认证流程是一样的
二.权限
认证. user_type 有1234的时候可以访问
问题:不用视图不用权限可以访问
也是通过dispatch到4,
到check_permissions
遇到的问题1
在设置超级vip时,返回的值是1
if request.user.user_type != 1:
return HttpResponse('无权访回')
AttributeError: 'NoneType' object has no attribute 'user_type'
因为我权限里给他设置了
# 全局使用的认证类
# 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', 'app01.utils.auth.Authtication' ],
'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.FirstAuthtication', ],
返回的是空,而不是request.user和 request.auth,所以返回的是空
设置超级vip
根据每一个对象的user_type判断, 有的给他一个超级vip,别的默认不处理,(这里1是vip)
if request.user.user_type != 1:
return HttpResponse('无权访回')
放入类中封装
# 认证过之后就开始权限认证了
class MyPermission(object):
# self.dispatch
def has_permission(self, request, view):
# 把权限写入类里面
if request.user.user_type != 1:
return False
return True # 有权访问
class OrderView(APIView):
# 局部权限
permission_classes = [MyPermission, ]
认证的所有的开启全局的之后,就没法处理auth了吗?
class MyPermission1(object):
def has_permission(self, request, view):
# 把权限写入类里面
if request.user.user_type == 3:
return False
return True # 有权访问
class UserInfoView(APIView):
"""订单相关业务(普通用户/vip)"""
# 类似于中间件, 装饰器的处理restframework的方法,认证登录方法
# authentication_classes = []
permission_classes = [MyPermission1, ]
def get(self, request, *args, **kwargs):
return HttpResponse('用户信息')
完成了基本的权限了
看源码流程
dispatch
initial
check_permissions
# [权限类的对象,权限类的对象,]
for permission in self.get_permissions():
# 通过per...为ture, 则not per..为false,不走, 而为false,则为ture, 走里面,抛异常
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
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()
raise exceptions.PermissionDenied(detail=message)
写成全局的
utils
-auth.py
-permission.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ['app01.utils.permission.SVIPPermission'] # 全局的
}
# 认证过之后就开始权限认证了
class SVIPPermission(object):
# self.dispatch
message = '必须是SVIP才能访问'
def has_permission(self, request, view):
# 把权限写入类里面
if request.user.user_type != 1:
return False
return True # 有权访问
class MyPermission1(object):
def has_permission(self, request, view):
# 把权限写入类里面
if request.user.user_type == 3:
return False
return True # 有权访问
如果写在全局,那么所有的都能用,如果不想用,字段换了就好了
# permission_classes = [MyPermission1, ]
有没有内置的权限呢?
有默认是return true
为了规范
from rest_framework.permissions import BasePermission
# 按照代码规范应该继承,内置的
class SVIPPermission(BasePermission):
别的权限,都是通过django写的
生产的环境,不可能是用默认的东西,都是自己去写(定制,而且级别也更高)
梳理:
- 1 . restframework 写权限的时候,最好写到类中,放到他的组件中
必须继承:BasePermission,必须实现: has_permission方法
-
返回值
return False #无权访问 return True #有权访问
-
局部:
class UserInfoView(APIView): """订单相关业务(普通用户/vip)""" permission_classes = [MyPermission1, ] #加上这个 def get(self, request, *args, **kwargs):
-
全局:
'DEFAULT_PERMISSION_CLASSES': ['app01.utils.permission.SVIPPermission']
下午:
三.节流
1,2 登陆之后才能看, 什么用户(vip还是svip)
3,访问频率
限制: 比如1分钟多少次 6次
保存了一个访问记录的字典 = {身份证号:[12:10:10,12:10:09,12:10:08]}
# 12:10:10,12:10:09,12:10:08,12:10:07,12:10:06,12:10:05
12:50:10
[12:10:10,12:10:09,12:10:08,] 可以清除
12:10:11
[12:10:10,12:10:09,12:10:08,] 就是大于3次了,不能了
根据ip,可以记录
访问记录 = {用户IP:[....]} //但是ip可以换啊, 没办法, 匿名用户, 爬虫
注册用户, 可以限制, 所以现在手机号绑定了,但是用户名多了,也没法办 .
class VisitThrottle(object):
def allow_request(self, request, view):
# return True# 可以继续访问 # return False 表示访问频率太高,被限制
return False
def wait(self):
pass
class AuthView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = [VisitThrottle,]
{
"detail": "Request was throttled."
}
错误,显示,没有get方法,用的是post,我一直在访问get,
{
"detail": "Method \"GET\" not allowed."
}
是因为没有post方法,我在找源码
wait()等待
def wait(self): # 提示,再等多少秒就能访问了
"""
还需要等多少秒才能访问
:return:
"""
ctime = time.time()
return 60 - (ctime - self.history[-1]) # 当前时间减去最后加入的时间
{
"detail": "Request was throttled. Expected available in 49 seconds."
}
给你这样显示,返回的值给了内部自定义的 一个 , 也可以自定义吧
import time
VISIT_RECORD = {}
class VisitThrottle(object):
"""60秒内只访问3次"""
def allow_request(self, request, view):
# 1.获取用户ip
remote_addr = request.META.get('REMOTE_ADDR')
ctime = time.time()
# remote_addr = request._request.get('REMOTE_ADDR')
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime, ]
return True
history = VISIT_RECORD.get(remote_addr)
self.history = history
while history and history[-1] < ctime - 60: # 有的值, 很可能是1千年以前,不作数,删掉
history.pop() # 60 < ctime - history[-1] # 留着
# 当大于60的时候,说明是一分钟以内,可以留着
if len(history) < 3:
# 访问把,当前时间加上去
history.insert(0, ctime)
return True
# return True 表示可以访问
# return False 表示访问频率太高,被限制
return False
def wait(self): # 提示,再等多少秒就能访问了
"""
还需要等多少秒才能访问
:return:
"""
ctime = time.time()
return 60 - (ctime - self.history[-1]) # 当前时间减去最后加入的时间
访问放到{} ,一刷新没了! 放到django的缓存中
节流源码
dispatch-->check_throttles
def check_throttles(self, request):
throttle_durations = []
for throttle in self.get_throttles():
if not throttle.allow_request(request, self): #所以自己写的allow_request.返回True和false , True时不走(not True)下面(异常),false(not flase) 走下面异常
throttle_durations.append(throttle.wait()) #原来是将wait()传给wait了
if throttle_durations:
self.throttled(request, max(throttle_durations)) # 这里用最大值,比较好,用最后一个也行, 反正是列表里的一个,时间wait()返回的时间
def throttled(self, request, wait):
"""
If request is throttled, determine what kind of exception to raise.
"""
raise exceptions.Throttled(wait)
# 然后wait返回的时间拼接成时间, 异常 , wait是用的反射吗? 也不是啊, 是protity方法吗
class Throttled(APIException):
status_code = status.HTTP_429_TOO_MANY_REQUESTS
default_detail = _('Request was throttled.')
extra_detail_singular = _('Expected available in {wait} second.')
extra_detail_plural = _('Expected available in {wait} seconds.')
default_code = 'throttled'
def __init__(self, wait=None, detail=None, code=None):
if detail is None:
detail = force_text(self.default_detail)
if wait is not None:
wait = math.ceil(wait)
detail = ' '.join((
detail,
force_text(ngettext(self.extra_detail_singular.format(wait=wait),
self.extra_detail_plural.format(wait=wait),
wait))))
self.wait = wait
super().__init__(detail, code)
找到了 {wait}的时间了 , 但是为什么是wait,不是wait(),不是方法吗?
throttle_durations.append(throttle.wait()) #原来是将wait()传给wait了
在check_throttles函数中
全局配置(从Apiview里找参数,加到settings里)
utils
auth.py
permission.py
throttle.py
'DEFAULT_THROTTLE_CLASSES': ['app01.utils.throttle.VisitThrottle'] # 所有的都是基于ip做的
基于内置的可以修改
get_cache_key 没写, 预留给你写的
class SimpleRateThrottle(BaseThrottle):
cache = default_cache # django内置的缓存
timer = time.time
cache_format = 'throttle_%(scope)s_%(ident)s'
scope = None
THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
def __init__(self)
def get_cache_key(self, request, view):
raise NotImplementedError('.get_cache_key() must be overridden') # 重写
def get_rate(self)
def parse_rate(self, rate)
def allow_request(self, request, view)
def throttle_success(self)
def throttle_failure(self)
def wait(self)
别的都给写了,说明不需要定制,死的, key可以根据自定义 ip 身份证等
distatch -> allow_requset() ,
cache 是缓存
cache = default_cache # 缓存 不知道怎么处理的
def __init__(self):
if not getattr(self, 'rate', None):
# '3/m'
self.rate = self.get_rate() # 3次 , 60秒
self.num_requests, self.duration = self.parse_rate(self.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)
num, period = rate.split('/')
num_requests = int(num)
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] # 也可以 '3/minu'取零个,都能取到值
return (num_requests, duration) # 3次 60 秒
def allow_request(self, request, view):
if self.rate is None:
return True
self.key = self.get_cache_key(request, view) # ip
if self.key is None:
return True
self.history = self.cache.get(self.key, []) # 根据ip的key取值
self.now = self.timer()
# Drop any requests from the history which have now passed the
# throttle duration
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
if len(self.history) >= self.num_requests:
return self.throttle_failure()
return self.throttle_success()
throttle_success # 成功之后
throttle_failure # 没有节流
wait # 等的时间都有
缓存看不懂 (内部django的)
# 用他自己的, 通过改配置文件,就可以控制频率,封装的更好
class VisitThrottle(SimpleRateThrottle):
scope = 'luffy' # 当key用的
def get_cache_key(self, request, view):
return self.get_ident(request)
'DEFAULT_THROTTLE_RATES': {
'Luffy': '3/m', # 3次, 每分钟
"LuffyUser": '10/m',
}
按照用户的访问频率限制
'DEFAULT_THROTTLE_CLASSES': ['app01.utils.throttle.UserThrottle'], # 只登录用户的
# 用户访问的频率
class UserThrottle(SimpleRateThrottle):
scope = 'LuffyUser'
def get_cache_key(self, request, view):
return request.user.username
from app01.utils.throttle import VisitThrottle
class AuthView(APIView):
throttle_classes = [VisitThrottle, ] # VisitThrottle
梳理:
a.基本使用
- 类 , 继承 : BaseThrottle , 实现 : allow_request, wait
- 类 , 继承 : SimpleRateThrottle, 实现 : get_cache_key scope = 'LuffyUser'(配置文件中的key)
b.局部认证
用于用户登录认证
throttle_classes = [VisitThrottle, ] # VisitThrottle
c.全局
'DEFAULT_THROTTLE_RATES': {
'Luffy': '3/m', # 3次, 每分钟
"LuffyUser": '10/m',
}
认证的时候, 有一个reqeust.user好像麻烦点的
面试题:
今日作业
昨天
-
1.中间件
-
2.Csrf
-
3.cBV
-
4.规范
10条规范
认识
-
5.djangoreac-ramework
如何验证(基于数据摩实现用户认证)
深码流程(向对象回赎流程)
今天上午:
-
1.中间件
-
2.crf漂
-
3.cst10范
-
4.面向对象
-
5.d1ango请求声明,期
-
6.dang请求声明同期(包含 reet framework框架
PS: dispatch
-
7.rest framework 认证流程(封装Request)
-
8.rest framework 权限流程
-
9.rest framework 节流流程
文档的形式记录下来
写文本:(所有的)
代码:
认证demo
权限demo(用户类型不同,权限不同)
节流demo(匿名, 登录用)
三组件组合