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返回

流程图如下:

 

总结:

  1. 中间件的process_request方法是在执行视图函数之前执行的。
  2. 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
  3. 不同中间件之间传递的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

 

posted @ 2018-09-17 22:46  Roc_Atlantis  阅读(216)  评论(0编辑  收藏  举报