一次请求的生命周期
image.png中间件
从图上可知,请求在到达视图之前,会依次执行中间件,视图返回的响应,依次倒序执行中间件。
在django中,中间件的是一个个的类,类中定义了一些方法。在django项目的setting.py文件中,有一个MIDDLEWARE列表,其中每一个元素都是一个中间件。
image.png
如果需要对请求作统一处理,比如在执行视图前,验证登录,日志记录,我们可以用装饰器,但是很麻烦,每个视图都要手动加装饰器语法。这时如果用中间件会更方便。
中间件的执行原理
MIDDLEWARE列表中的每一个元素都是字符串,
观察中间件元素发现由两部分构成:路径+类名;因此如果能分离出路径,并根据路径导入模块,然后执行模块中类的方法,就可以了。
关键问题是路径和类名都是字符串,通过字符串路径导入模块要借助importlib模块;在模块中通过字符串拿到类和在类中根据字符串拿到方法,都可以通过反射来做。
下面我们来自己模拟一下:
PLUGIN = {
'disk': 'plugin.disk.Disk',
'memory': 'plugin.memory.Memory',
'network': 'plugin.network.Network',
}
import importlib # 通过字符串导入模块
for k, v in PLUGIN.items():
# 分离模块路径 和 类名:
# 'plugin.disk.Disk' --> 模块路径'plugin.disk' 类名 'Disk' (结果都是字符串)
# 通过字符串路径导入模块 importlib
# 通过反射获取模块中的类
module_name, cls_name = v.rsplit('.', maxsplit=1)
module = importlib.import_module(module_name)
cls = getattr(module, cls_name)
obj = cls() # 实例化对象
res = obj.method() # 调用对象的绑定方法
自定义中间件
下面我们自定义一个中间件,用于用户验证:
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 M1(MiddlewareMixin): # 自定义类,继承MiddlewareMixin
def process_request(self, request):
print('--- 自定义中间件进行登录验证 ---')
# 如果访问路径是login函数,放行;否则验证session信息
if request.path_info == 'login.html':
return None
from django.shortcuts import redirect
if not request.session.get('user_info'):
return redirect('/login.html')
def process_response(self, request, response):
print('自定义逻辑')
return response
注意,自定义类时,类中的方法和参数都是固定的。
定义完成后,在setting.py中找到MIDDLEWARE列表,将我们刚刚自定义的中间件路径添加进去:
image.png
中间件中的方法
-process_request 请求进来时执行;不能有返回值,一旦某个中间件的process_request 有返回值,那么下面的中间件就不走了,后面的url路由,视图函数更不会执行,直接执行当前中间件的process_response方法,层层往上返回这个返回值(如果process_response返回了其它值,或者上面的中间件返回了其它值,最后执行的返回值会覆盖之前的返回值。
class M1(MiddlewareMixin):
def process_request(self, request, response):
from django.shortcuts import HttpResponse
allowed_ip = ['192.168.0.11',]
# 维护一个允许/禁止访问的ip地址列表,判断请求ip, 放行/阻拦
if request.META.get('REMOTE_ADDR') not in allowed_ip:
return HttpResponse('请回去吧')
else:
return None
def process_response(self, request, response):
return response
-process_response 返回响应时执行,默认情况下返回response, 即视图函数的返回值,如果在这个方法中自定义了返回值,将返回自定义返回值。
class M1(MiddlewareMixin):
def process_response(self, request, response):
print('自定义逻辑')
return response
-process_view
class M1(MiddlewareMixin):
def process_view(self, request, view_func, view_func_args, view_func_kwargs):
print('M2.process_view')
执行时机:执行完process_request – > 路由分发, – > process_view –> 视图函数 –> process_response。process_view一旦有返回值,那么跳过下面的中间件,开始从视图层层往上,返回响应。假设有多个中间件,每个中间件中都定义了以上三个方法,那么django执行中间件的逻辑其实相当于将每个中间件中的一类方法放到一个列表里,一共3个列表,循环列表执行其中的方法,过程如下:
for process_request in process_request_list:
process_request()
执行路由分发 url
for process_view in process_view_list:
process_view()
执行视图 func()
for process_response in process_response_list:
process_response()
-process_exception 默认不执行;如果视图中出现异常,就会被执行。应用场景:自定义一个”更友好“的错误页面;
class M2(MiddlewareMixin):
def process_exception(self, request, exception):
from django.shortcuts import HttpResponse
return HttpResponse('开发该功能的程序员已被关小黑屋')
-process_template_response 默认不执行;如果返回的对象有render方法,就会被执行。不常用。
class MyResponse(object): # 我们可以在自定义类中对响应作进一步处理
def __init__(self, response):
self.response = response
def render(self):
return self.response
def demo(request):
response = HttpResponse('OK')
return MyResponse(response) # 将响应作为字定类的的对象返回,拥有render方法
网友评论