Django 中间件
一、请求生命周期:
以下图是客户端发起一次http请求的流程:
注意:django框架不包括socket,要借助图中两个模块实现socket,进行客户端和服务器通信。wsgi性能较弱,一般测试开发用;企业中用uwsgi比较好,性能强,并发好
。
二、中间件简介:
上图可知,在请求到达视图之前,会依次执行中间件,视图返回的响应,依次倒序执行中间件
。
django 中间件(middleware),在django中其实就是一个类
,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
在django项目的setting.py文件中,有一个MIDDLEWARE列表,其中每一个元素都是一个中间件
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'qinspect.middleware.QueryInspectMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
若需要对请求作统一处理,比如执行,验证登录、日志记录、获取CBV响应时间,我们可以使用装饰器,但是很麻烦,每个视图都要手动加装饰器,这时使用中间件会更方便。
三、中间件应用:
1.做IP限制:
- 放在中间件类的列表中,阻止某些IP访问
2.URL访问过滤:
- 如果用户访问的是login视图(直接pass)
- 如果访问的其他视图(需要检测是不是有session已经存在,那么pass,没有返回login进行登录)
3.缓存:
- 客户端请求来了,中间件去缓存查看是否有数据,若有直接返回给客户端,没有再去逻辑层执行视图函数
四、自定义中间件:
下面我们自定义一个中间件,获取每个接口响应时间:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import time
from django.utils.deprecation import MiddlewareMixin
from libs.api_tools import slow_api_utils
class CustomizeMiddleware(MiddlewareMixin):
"""自定义中间件: 获取所有接口的处理时间."""
def process_request(self, request):
# 获取这个请求的开始时间
self.start_time = time.time()
def process_response(self, request, response):
end_time = time.time()
# 结束时间 - request 开始时间 = 接口的处理时间
process_time = end_time - self.start_time
# 下面是处理process_time 时间发起异步请求, 这里省略......
slow_api_utils.judge_max_process_time(request, process_time)
return response
在setting.py中注册中间件:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'qinspect.middleware.QueryInspectMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'libs.api_tools.middleware.CustomizeMiddleware' # 创建的自定义中间件类(路径 + 类名)
]
注意:django 1.10之后django无法导入,因此直接把MiddlewareMixin类拿过来继承就好了:
class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
class CustomizeMiddleware(MiddlewareMixin): # 自定义类,继承MiddlewareMixin
......
同样也是在setting.py中注册中间件:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'qinspect.middleware.QueryInspectMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'libs.api_tools.middleware.CustomizeMiddleware' # 创建的自定义中间件类(路径 + 类名)
]
五、中间件五个方法:
process_request 请求进来时执行;
-
不能有返回值,若在process_request有返回值,那么下面的中间件将不会执行,后面的URL以及视图函数也不会执行,直接执行当前的中间件的process_response方法。
# 请求时,仅让IP为192.168.1.1才可以进行访问 from django.shortcuts import HttpResponse class CustomizeMiddleware(MiddlewareMixin): def process_request(self, request, response): allowed_ip = ['192.168.1.1',] # 允许/禁止访问的ip地址列表,判断请求ip, 放行/阻拦 if request.META.get('REMOTE_ADDR') not in allowed_ip: return HttpResponse('您的IP地址无权访问') else: return None def process_response(self, request, response): return response
process_response 返回响应时执行;
-
默认情况下返回response, 即视图函数的返回值,如果在这个方法中自定义了返回值,将返回自定义返回值。
class CustomizeMiddleware(MiddlewareMixin): def process_response(self, request, response): print('处理一些逻辑条件等......') return response
process_view 视图函数;
-
执行过程:
执行完process_request → 路由分发(dispatch) → process_view → 视图函数 → process_response
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class CustomizeMiddleware1(MiddlewareMixin): def process_request(self,request): print("...来自中间件1的请求...") def process_response(self,request,response): print("...来自中间件1的返回...") return response def process_view(self, request, callback, callback_args, callback_kwargs): print("...中间件1view...") class CustomizeMiddleware2(MiddlewareMixin): def process_request(self,request): print("...来自中间件2的请求...") def process_response(self,request,response): print("...来自中间件2的返回...") return response def process_view(self, request, callback, callback_args, callback_kwargs): print("...中间件2view...") class CustomizeMiddleware3(MiddlewareMixin): def process_request(self,request): print("...来自中间件3的请求...") def process_response(self,request,response): print("...来自中间件2的返回...") return response def process_view(self, request, callback, callback_args, callback_kwargs): print("...中间件3view...")
# 输出结果为: ...来自中间件1的请求.. ...来自中间件2的请求.. ...来自中间件3的请求.. ...中间件1view... ...中间件2view... ...中间件3view... ...来自中间件3的返回... ...来自中间件2的返回... ...来自中间件1的返回...
如图所示:
-
当最后一个中间的process_request到达路由关系映射之后,返回到中间件1的process_view,然后依次往下,到达views函数,最后通过process_response依次返回到达用户。
process_exception 视图中出现异常,就会被执行(默认不执行);
# 定义一个提示错误信息页面
from django.shortcuts import HttpResponse
class CustomizeMiddleware(MiddlewareMixin):
def process_exception(self, request, exception):
return HttpResponse('在处理中间件时,抛出了异常,就会走这里哦~')
process_template_response 若返回的对象有render方法,就会被执行(默认不执行);
process_template_response(self, request, response)
- 它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)
- process_template_response是在
视图函数执行完成后立即执行
,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法
(或者表明该对象是一个TemplateResponse对象或等价方法) - 视图函数执行完之后,立即执行了中间件的process_template_response方法,顺序是倒序,先执行CustomizeMiddleware3的,再执行CustomizeMiddleware2的,接着执行了视图函数返回的HttpResponse对象的render方法,返回了一个新的HttpResponse对象,接着执行中间件的process_response方法。