Py006-02-07中间件

中间件(django里非常非常重要的一个内容)

中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

Django默认的7个Middleware(中间件):

settings.py里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# 通过导入模块查看中间件源码
from django.middleware.security import SecurityMiddleware

# 点进去发现基本都有这两个方法
'''
def process_request(self,request):...

def process_response(self,request,response):...
'''

一次请求的完成过程

1
2
3
django接受请求时  中间件时  依次执行  process_request

django响应时 中间件时 逆向依次执行 process_response

自定义中间件

中间件中一共有四个方法:

1
2
3
4
process_request
process_view
process_exception
process_response

我们也可以自己定义一个中间件,我们可以自己写一个类,但是必须继承MiddlewareMixin
需要导入

1
from django.utils.deprecation import MiddlewareMixin

step001 第一步创建一个项目建立一个应用app01

在app01目录下建立自己的中间件

my_middlewares.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 自定义中间件 必须继承MiddlewareMixin
# 自定义中间件 必须继承MiddlewareMixin
# 自定义中间件 必须继承MiddlewareMixin

from django.utils.deprecation import MiddlewareMixin

class MyMiddleWare1(MiddlewareMixin):
def process_request(self,request):
print('my middleware 001 request')

def process_response(self,request,response):
print('my middleware 001 response')
return response

class MyMiddleWare2(MiddlewareMixin):
def process_request(self,request):
print('my middleware 002 request')

def process_response(self, request, response):
print('my middleware 002 response')
return response

step002 第二步settings.py里引入自定义的中间件

1
2
3
4
5
6
7
8
9
10
11
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'app01.my_middleware1.MyMiddleWare1',
'app01.my_middleware1.MyMiddleWare2'
]

访问 http://localhost:8000/index/

1
2
3
4
5
6
# 打印如下信息
my middleware 001 request
my middleware 002 request
index.html
my middleware 002 response
my middleware 001 response

为什么process_request没返回值

return一下试试

给 MyMiddleWare1的process_request()添加一个返回值

1
2
3
4
5
6
7
from django.shortcuts import render,HttpResponse

class MyMiddleWare1(MiddlewareMixin):

def process_request(self,request):
print('my middleware 001 request')
return HttpResponse('被禁止的请求')

访问 http://localhost:8000/index/

1
2
3
4
5
6
# 控制台打印
my middleware 001 request
my middleware 001 response

# 页面显示
被禁止的请求

第一个中间件由于return一个HttpResponse(‘xxx’)

导致不会再去下一个中间件而提前返回给客户端

process_view

1
process_view(self, request, callback, callback_args, callback_kwargs)

中间件修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyMiddleWare1(MiddlewareMixin):

def process_request(self,request):
print('my middleware 001 request')

def process_response(self,request,response):
print('my middleware 001 response')
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md1view view")

class MyMiddleWare2(MiddlewareMixin):

def process_request(self,request):
print('my middleware 002 request')

def process_response(self, request, response):
print('my middleware 002 response')
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md1view2 view")

访问 http://localhost:8000/index/

1
2
3
4
5
6
7
my middleware 001 request
my middleware 002 request
Md1view view
Md1view2 view
index.html
my middleware 002 response
my middleware 001 response

当最后一个中间的process_request到达路由关系映射之后,返回到中间件1的process_view,然后依次往下,到达views函数,最后通过process_response依次返回到达用户。

process_view可以用来调用视图函数:

在第二个中间件里做如下处理

  • 直接返回结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyMiddleWare2(MiddlewareMixin):
...
def process_view(self, request, callback, callback_args, callback_kwargs):

print("Md1view2 view")
# 直接返回响应体内容
return HttpResponse('view2 response')
'''
# 就不会进入路由views.index的视图函数了
my middleware 001 request
my middleware 002 request
Md1view view
Md1view2 view
my middleware 002 response
my middleware 001 response
'''
  • 提前获取路由里 index函数的响应
1
2
3
4
5
6
7
8
class MyMiddleWare2(MiddlewareMixin):
...

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md1view2 view")
# 拿到视图函数的结果提前响应
res = callback(request, *callback_args, **callback_kwargs)
return res
1
2
3
4
5
6
7
my middleware 001 request
my middleware 002 request
Md1view view
Md1view2 view
index.html
my middleware 002 response
my middleware 001 response

注意:process_view如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。

process_exception

正常是不会执行process_exception里的内容,只有报错的时候才会执行

1
process_exception(self, request, exception)

添加process_exception函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class MyMiddleWare1(MiddlewareMixin):

def process_request(self,request):
print('my middleware 001 request')

def process_response(self,request,response):
print('my middleware 001 response')
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md1view view")

def process_exception(self,request,exception):
print("md1 process_exception...")

class MyMiddleWare2(MiddlewareMixin):

def process_request(self,request):
print('my middleware 002 request')

def process_response(self, request, response):
print('my middleware 002 response')
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md1view2 view")

def process_exception(self,request,exception):
print("md2 process_exception...")


'''
访问 http://localhost:8000/index/

my middleware 001 request
my middleware 002 request
Md1view view
Md1view2 view
index.html
my middleware 002 response
my middleware 001 response
'''

由于没有错误所以不会执行process_exception的内容

修改views.py 添加一个未初始化的变量

1
2
3
4
def index(request):
print('index.html')
aaa
return render(request,'index.html')
1
2
3
4
5
6
7
8
9
10
my middleware 001 request
my middleware 002 request
Md1view view
Md1view2 view
index.html
md2 process_exception...
md1 process_exception...
....错误信息
my middleware 002 response
my middleware 001 response

系统返回的错误信息难看了,想自己返回一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# return响应信息
class MyMiddleWare2(MiddlewareMixin):
...
def process_exception(self,request,exception):
print("md2 process_exception...")
return HttpResponse('error')

'''
my middleware 001 request
my middleware 002 request
Md1view view
Md1view2 view
index.html
md2 process_exception...
my middleware 002 response
my middleware 001 response
'''

流程如下

三、应用案例

  1. 做IP访问频率限制

某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

  1. URL访问过滤

如果用户访问的是login视图(放过)
如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!

中间件是个双刃剑

1
2
如果80个视图函数70个不需要校验
如果用中间件处理 反而影响了效率

作为延伸扩展内容,有余力的同学可以尝试着读一下以下两个自带的中间件:

1
2
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',