https://www.jb51.net/article/136422.htm
https://www.jb51.net/article/143832.htm
https://www.jb51.net/article/69953.htmweb
我对django
中间件的理解:以组件化的形式,为大量的请求或响应提供批量化处理的接口,封装着可插拔式的独立附加功能逻辑,与基本web
业务逻辑功能解耦,经过hook
函数能更细致的处理请求或响应过程。django
django
的中间件有以下特色:
一、每一个中间件由一个类来表示
二、中间件的逻辑必须写在特定的接口中,这些接口被称为hook
函数
三、中间件的执行有顺序依赖
四、hook
函数的执行有规定顺序
五、中间件的启用会影响全部的请求/响应
六、中间件是可插拔式的,这意味着能够不启用任何中间件
七、中间件应该仅做为数据过滤器的角色对数据过滤、转换、清洗,对数据的业务处理应该放在视图系统中
八、如第7点,中间件应该做为额外功能模块介入请求/响应流程,与普通业务处理模块(视图系统)解耦设计模式
中间件在django
框架中的定位图
浏览器
配置中间件类缓存
from django.utils.deprecation import MiddlewareMixin class MyMiddleware(MiddlewareMixin): ''' 自定义类名,继承内置的中间件混合类。 hook函数有固定的接口,自定义逻辑处理代码 ''' def process_request(self, request): pass def process_view(self, request, callback, callback_args, callback_kwargs): pass def process_exception(self, request, exception): pass def process_template_response(self, request, response): return response def process_response(self, request, response): return response
编写中间件hook
函数逻辑服务器
一、process_request(self, request)
参数request
是HttpRequest
对象,此hook
函数将会在路由分发前执行,有两类返回值:session
1. return None # 请求流程将会继续按照原计划执行,这应该是默认设置 2. return HttpResponse # 请求将会跳转到当前中间件的process_response函数处理并进入响应流程 注意:虽然return一个非None且非HttpResonse的值也会使得流程跳转到响应流程,不过并不建议这么作,由于每个process_response函数都指望接收到一个HttpResponse对象以便作进一步的处理,而不是收到一个奇怪的字符串或者数字。 注意:进入响应流程的入口是当前中间件的process_response
二、process_view(self, request, callback, callback_args, callback_kwargs)
请求流程完成路由分发后,在执行视图函数前将会执行此hook
函数。此函数的callback
是对路由分发肯定的视图函数的引用,callback_args
, callback_kwargs
是传递给视图函数的参数,有两类返回值:闭包
1. return None # 请求流程将会按照原计划继续,这应该是默认设置 2.return HttpResponse # 请求将会跳转到最后一个中间件的process_response函数处理并进入响应流程 注意:进入响应流程的入口是最后一个中间件的process_response
三、process_template_response
在 view
视图函数中使用 render
渲染一个模版对象完成以后被调用,它必须返回一个render
方法执行后的response
对象。并发
四、process_exception(self, request, exception)
当视图函数执行出错的时候,会把错误抛给此hook
函数,有两类返回值:
1. return None # 将会把错误对象exception提交给前一个中间件的process_exception处理 2. return HttpResponse # 将会跳转到最后一个中间件的process_response函数处理并进入响应流程 注意:不该该return exception 注意:进入响应流程的入口是最后一个中间件的process_response
五、process_response(self, request, response)
此hook
函数将在响应流程中执行,函数必须返回HttpResponse对象
return HttpResponse # 把响应对象交给前一个中间件的process_response函数处理,若是已是第一个中间件,将会交给wsgi服务器处理并发送给用户浏览器。 注意:必须返回HttpResponse对象
启用中间件
在项目settings文件中添加对中间件类的引用以启动中间件
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_middlewares.MyMiddleware', # 添加对自定义中间件类的引用以启动 ]
中间件及hook
函数执行流程(省略process_template_response
)
中间件的功能划分遵循原则:视图函数仅完成本应完成的工做,额外的功能经过中间件来单独提供。
中间件是可插拔式即意味着中间件的启用和禁用均不会影响视图函数的原始工做,这很是像以前学习过的python
装饰器。python
装饰器实现了设计模式中的装饰模式,装饰器的目的是:在保持原有函数功能的基础之上,新增额外的功能,且新增的功能应该与原函数功能解耦,装饰器也能够有选择的增长或者移除。经过本身的研究和网上各大神的博客学习中发现,django
的中间件其实也是一种装饰模式,并且能够和python
的装饰器用法高度适配,我用以下两张图来对django
中间件和装饰器进行了转换。
图1、django
中间件到装饰器的转换
图2、django
中间件到装饰器的转换
python
多重装饰器
虽然还没研究过django
中间件的源代码,不过我想先尝试着使用python
的装饰器来模拟中间件的效果。
首先,要理解装饰器的核心知识:利用闭包特性来保存内层函数的执行上下文。正由于闭包的存在,内层函数的执行上下文(执行环境)即便在外层函数结束后依然能够被保存,这就意味着在外层函数结束后,依然能够正确的执行内层函数。 (ps:若是不使用闭包,外层函数结束后,该函数中的全部变量都会被销毁)
其次,装饰器能够迭代使用。
迭代是重复反馈过程的活动,其目的一般是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代获得的结果会做为下一次迭代的初始值。
---百度百科
就像这样:
@IPFilter @UserAuth @DataTransform @TrafficLog def index(request): # somecode... return response
利用装饰器函数模拟中间件效果
如今咱们经过一个多重函数装饰器简单的模拟一下中间件的效果,需求以下:
有一个ip
黑名单列表,列表中的ip
不能访问页面。此外,有三个函数须要定义:
一个简单的show_page
函数,将会模拟用户访问某一个页面,并返回简单的内容(当前用户的ip
)。
一个filter_ip
装饰器,过滤恶意ip
,若是用户ip
在黑名单中就没法正常访问页面。
一个traffic_log
装饰器,对正常访问的流量进行统计。
基础需求:经过自定义一个request
对象模拟用户浏览器发出的http
请求对象,request
直接执行show_page
视图函数以获得指望访问的http
页面。
额外需求:经过添加以上两个装饰器来增长ip
过滤和流量统计的功能。
代码定义以下:
# 黑名单的定义 black_ip_list = ['10.1.1.1', '172.16.1.1', '192.168.1.1'] # 这里简单的使用全局变量来表示统计流量 traffic_count = 0 # request对象的定义 class Request(object): def __init__(self, source_ip): self.source_ip = source_ip # filter_ip过滤器函数的定义 def filter_ip(func): def inner(request): source_ip = request.source_ip if source_ip in black_ip_list: response = '你的ip在黑名单中' else: response = func(request) return response return inner # traffic_log流量统计函数的定义 def traffic_log(func): def inner(request): global traffic_count traffic_count += 1 print('当前页面被有效请求的次数是:', traffic_count) response = func(request) return response return inner # show_page视图函数的定义 def show_page(request): source_ip = request.source_ip response = '模拟的目标页面内容,此用户的ip是-->' + source_ip return response
结果1,实现最基本的用户访问
结果2,实现ip
黑名单过滤
结果3,实现有效流量统计
结果4,实现ip
黑名单过滤+有效流量统计(特别注意顺序依赖)
利用装饰器类模拟中间件效果
虽然简单的模拟出了中间件的可插拔、功能解耦、批量请求处理等功能,但还作的不够好,咱们能够基于上面的代码,再作一些必要的封装,代码以下:
class TrafficLogMiddleware(object): traffic_count = 0 def __init__(self, func): self.func = func def __call__(self, request): self.traffic_count += 1 print('当前页面被有效请求的次数是:', self.traffic_count) response = self.func(request) return response class FilterIPMiddleware(object): black_ip_list = ['10.1.1.1', '172.16.1.1', '192.168.1.1'] def __init__(self, func): self.func = func def __call__(self, request): source_ip = request.source_ip if source_ip in self.black_ip_list: response = '你的ip在黑名单中' else: response = self.func(request) return response class Request(object): def __init__(self, source_ip): self.source_ip = source_ip @FilterIPMiddleware @TrafficLogMiddleware def show_page(request): source_ip = request.source_ip response = '模拟的目标页面内容,此用户的ip是-->' + source_ip return response
感受不像django
的中间件接口?能够这样写:
class Middleware(object): def __init__(self, func): self.func = func def __call__(self, request): response = self.process_request(request) if not response: response = self.func(request) response = self.process_response(request, response) return response def process_request(self, request): pass def process_response(self, request, response): return response class TrafficLogMiddleware(Middleware): traffic_count = 0 def process_request(self, request): self.traffic_count += 1 print('当前页面被有效请求的次数是:', self.traffic_count) def process_response(self, request, response): return response class FilterIPMiddleware(Middleware): black_ip_list = ['10.1.1.1', '172.16.1.1', '192.168.1.1'] def process_request(self, request): source_ip = request.source_ip if source_ip in self.black_ip_list: response = '你的ip在黑名单中' else: response = None return response def process_response(self, request, response): return response class Request(object): def __init__(self, source_ip): self.source_ip = source_ip @FilterIPMiddleware @TrafficLogMiddleware def show_page(request): source_ip = request.source_ip response = '模拟的目标页面内容,此用户的ip是-->' + source_ip return response
执行结果以下:
中间件的启用会影响全部的请求/响应--->适用于大量请求/响应的批量化处理场景
中间件相互之间功能解耦,顺序依赖--->适合可插拔式的业务场景
中间件能够介入请求/响应流程--->适用于须要更加细致化处理请求/响应流程的业务场景
一、流量统计
二、恶意ip过滤
三、用户区分
四、缓存CDN
五、URL过滤
六、数据预处理
......
django
框架内置了7个中间件,用于提供基本的http
请求和响应处理,内置中间件的基本学习能够参考:
https://www.jb51.net/article/69953.htm
一、装饰器和中间件都实现了装饰模式,此模式的目的是为了在不修改原有模块的条件下新增功能代码,并能够提供可插拔的效果,同时新增代码和原有代码功能上解耦。 二、类比学习很重要,能够同时提高对两个同类知识的理解。 三、中间件的角色应该是数据清洗/过滤/转换器,不该该在中间件上处理业务逻辑,而只是处理数据约束,具体的业务逻辑应该放置在视图函数中,这也是它的本职工做。 四、不要滥用中间件,过多的中间件会增长请求/响应流程的环节数,发生错误的时候提高排错难度。中间件的使用应该依赖业务场景,在最合适的地方使用最合适的技术,才能发挥最高的效率。