Django之中间件
1、中间件是什么?
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
2、做过什么?
用户登录
日志记录
crsf:对所有的post请求做了一个验证
session
权限管理
3、
注意:
对于所有请求的批量做处理的时候用中间件
单独对某几个函数做处理的时候用装饰器,后面会有详述
每一个中间件都有其对应的功能
4、使用步骤:
1 步骤: 2 1、、先在创建的app下建一个文件夹,里面写一个py文件 3 2、、然后开始写类 4 1.中间件就是一个类,类里面写几个方法 5 class M1(MiddlewareMixin): 必须继承 6 def process_request(self,request): request:请求里面的所有的东西 7 print("m1.request_request") 8 这个方法里面别轻易返回值,要是有返回值就不再继续执行后面的了,执行自己的process_response和上边的response 9 一般是无返回值的:继续执行后续的中间件和视图函数 10 def process_response(self,request,response): 11 return response 12 13 2.在settings中的MIDDLEWARE加上路径 14 文件夹名称.py文件名称.类名 15 3.找到继承的那个类,把那个类拿过来 16 一般不要用导入的方法,不然有时候更新了就没有这个类了,你就把它继承的那个类拿过来,
自定义中间件主要用到的几个方法(1、2、5比较常用)
1 process_request(self,request) 2 3 process_view(self, request, callback, callback_args, callback_kwargs) 4 5 process_template_response(self,request,response) 6 7 process_exception(self, request, exception) 8 9 process_response(self, request, response)
process_request和process_response
当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求时process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。
上面的这些中间件都是Django自带的中间件,我们也可以自定义中间件。
自定义中间件必须继承MiddlewareMixin
定义的中间件本质其实就是一个类。
自定义中间件的步骤如下:
1.导入模块MiddlewareMixin
from
django.utils.deprecation
import
MiddlewareMixin
2.自定义中间件
1 from django.utils.deprecation import MiddlewareMixin 2 from django.shortcuts import HttpResponse 3 4 class Md1(MiddlewareMixin): 5 6 def process_request(self,request): 7 print("Md1请求") 8 9 def process_response(self,request,response): 10 print("Md1返回") 11 return response 12 13 class Md2(MiddlewareMixin): 14 15 def process_request(self,request): 16 print("Md2请求") 17 #return HttpResponse("Md2中断") 18 def process_response(self,request,response): 19 print("Md2返回") 20 return response
3.在views里定义视图函数index
def index(request): print("view函数...") return HttpResponse("OK")
4.在settings.py的MIDDLEWARE里注册自己定义的中间件
返回的结果:
Md1请求
Md2请求
view函数...
Md2返回
Md1返回
注意:如果当请求到达请求2的时候直接不符合条件返回,即return HttpResponse("Md2中断"),程序将把请求直接发给中间件2返回,然后依次返回到请求者,结果如下:
返回Md2中断的页面,后台打印如下:
Md1请求
Md2请求
Md2返回
Md1返回
流程图如下:
总结:
- 中间件的process_request方法是在执行视图函数之前执行的。
- 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
- 不同中间件之间传递的request都是同一个对象
多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。
process_view
process_view(self, request, view_func, view_args, view_kwargs)
该方法有四个参数
request是HttpRequest对象。
view_func是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)
view_args是将传递给视图的位置参数的列表.
view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
Django会在调用视图函数之前调用process_view方法。
它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。
1 from django.utils.deprecation import MiddlewareMixin 2 from django.shortcuts import HttpResponse 3 4 class Md1(MiddlewareMixin): 5 6 def process_request(self,request): 7 print("Md1请求") 8 #return HttpResponse("Md1中断") 9 def process_response(self,request,response): 10 print("Md1返回") 11 return response 12 13 def process_view(self, request, callback, callback_args, callback_kwargs): 14 print("Md1view") 15 16 class Md2(MiddlewareMixin): 17 18 def process_request(self,request): 19 print("Md2请求") 20 return HttpResponse("Md2中断") 21 def process_response(self,request,response): 22 print("Md2返回") 23 return response 24 25 def process_view(self, request, callback, callback_args, callback_kwargs): 26 print("Md2view")
执行结果如下:
Md1请求
Md2请求
Md1view
Md2view
view函数...
Md2返回
Md1返回
过程如下
process_view可以调用视图函数
1 class Md1(MiddlewareMixin): 2 3 def process_request(self,request): 4 print("Md1请求") 5 #return HttpResponse("Md1中断") 6 def process_response(self,request,response): 7 print("Md1返回") 8 return response 9 10 def process_view(self, request, callback, callback_args, callback_kwargs): 11 12 # return HttpResponse("hello") 13 14 response=callback(request,*callback_args,**callback_kwargs) 15 return response
执行结果如下
Md1请求
Md2请求
view函数...
Md2返回
Md1返回
注意:process_view如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。
process_exception
process_exception(self, request, exception)
该方法两个参数:
一个HttpRequest对象
一个exception是视图函数异常产生的Exception对象。
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
1 class Md1(MiddlewareMixin): 2 3 def process_request(self,request): 4 print("Md1请求") 5 #return HttpResponse("Md1中断") 6 def process_response(self,request,response): 7 print("Md1返回") 8 return response 9 10 def process_view(self, request, callback, callback_args, callback_kwargs): 11 12 # return HttpResponse("hello") 13 14 # response=callback(request,*callback_args,**callback_kwargs) 15 # return response 16 print("md1 process_view...") 17 18 def process_exception(self,request,exception): 19 print("md1 process_exception...") 20 21 22 23 class Md2(MiddlewareMixin): 24 25 def process_request(self,request): 26 print("Md2请求") 27 # return HttpResponse("Md2中断") 28 def process_response(self,request,response): 29 print("Md2返回") 30 return response 31 def process_view(self, request, callback, callback_args, callback_kwargs): 32 print("md2 process_view...") 33 34 def process_exception(self,request,exception): 35 print("md1 process_exception...")
没有错误执行结果如下:
Md1请求
Md2请求
md1 process_view...
md2 process_view...
view函数...
Md2返回
Md1返回
对第二个process_exception做出如下修改
def process_exception(self,request,exception): print("md2 process_exception...") return HttpResponse("error")
执行结果如下===》视图函数出错执行这个process_view就会直接执行process_response。。
Md1请求
Md2请求
md1 process_view...
md2 process_view...
view函数...
md2 process_exception...
Md2返回
Md1返回
当视图函数出现错误,流程图如下:
process_template_response(self,request,response)
该方法对视图函数返回值有要求,必须是一个含有render方法类的对象,才会执行此方法
1 class Test: 2 def __init__(self,status,msg): 3 self.status=status 4 self.msg=msg 5 def render(self): 6 import json 7 dic={'status':self.status,'msg':self.msg} 8 9 return HttpResponse(json.dumps(dic)) 10 def index(response): 11 return Test(True,'测试')
四 中间件应用场景
1、做IP访问频率限制
某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。
2、URL访问过滤
如果用户访问的是login视图(放过)
如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!
CSRF跨站伪造攻击==》见下方博客,写的非常好
https://www.cnblogs.com/liuqingzheng/p/9505044.html
在form表单中应用:
<form action="" method="post"> {% csrf_token %} <p>用户名:<input type="text" name="name"></p> <p>密码:<input type="text" name="password"></p> <p><input type="submit"></p> </form>
在Ajax中应用:
放在data里:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <script src="/static/jquery-3.3.1.js"></script> 6 <title>Title</title> 7 </head> 8 <body> 9 <form action="" method="post"> 10 {% csrf_token %} 11 <p>用户名:<input type="text" name="name"></p> 12 <p>密码:<input type="text" name="password" id="pwd"></p> 13 <p><input type="submit"></p> 14 </form> 15 <button class="btn">点我</button> 16 </body> 17 <script> 18 $(".btn").click(function () { 19 $.ajax({ 20 url: '', 21 type: 'post', 22 data: { 23 'name': $('[name="name"]').val(), 24 'password': $("#pwd").val(), 25 'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val() 26 }, 27 success: function (data) { 28 console.log(data) 29 } 30 31 }) 32 }) 33 </script> 34 </html>
放在cookie里:
获取cookie:document.cookie
是一个字符串,可以自己用js切割,也可以用jquery的插件
获取cookie:$.cookie('csrftoken')
设置cookie:$.cookie('key','value')
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <script src="/static/jquery-3.3.1.js"></script> 6 <script src="/static/jquery.cookie.js"></script> 7 <title>Title</title> 8 </head> 9 <body> 10 <form action="" method="post"> 11 {% csrf_token %} 12 <p>用户名:<input type="text" name="name"></p> 13 <p>密码:<input type="text" name="password" id="pwd"></p> 14 <p><input type="submit"></p> 15 </form> 16 <button class="btn">点我</button> 17 </body> 18 <script> 19 $(".btn").click(function () { 20 var token=$.cookie('csrftoken') 21 //var token='{{ csrf_token }}' 22 $.ajax({ 23 url: '', 24 headers:{'X-CSRFToken':token}, 25 type: 'post', 26 data: { 27 'name': $('[name="name"]').val(), 28 'password': $("#pwd").val(), 29 }, 30 success: function (data) { 31 console.log(data) 32 } 33 34 }) 35 }) 36 </script> 37 </html> 38 39 放在cookie里
其它操作
全站禁用:注释掉中间件 'django.middleware.csrf.CsrfViewMiddleware',
局部禁用:用装饰器(在FBV中使用)
1 from django.views.decorators.csrf import csrf_exempt,csrf_protect 2 # 不再检测,局部禁用(前提是全站使用) 3 # @csrf_exempt 4 # 检测,局部使用(前提是全站禁用) 5 # @csrf_protect 6 def csrf_token(request): 7 if request.method=='POST': 8 print(request.POST) 9 10 return HttpResponse('ok') 11 return render(request,'csrf_token.html')
在CBV中使用
1 # CBV中使用 2 from django.views import View 3 from django.views.decorators.csrf import csrf_exempt,csrf_protect 4 from django.utils.decorators import method_decorator 5 # CBV的csrf装饰器,只能加载类上(django的bug) 6 # 给get方法使用csrf_token检测 7 @method_decorator(csrf_exempt,name='get') 8 # 给post加 9 @method_decorator(csrf_exempt,name='post') 10 # 给所有方法加 11 @method_decorator(csrf_exempt,name='get') 12 class Foo(View): 13 def get(self,request): 14 pass 15 def post(self,request): 16 pass