零、参考

https://www.jb51.net/article/136422.htm

https://www.jb51.net/article/143832.htm

https://www.jb51.net/article/69953.htm

一、中间件的基本理解

我对django中间件的理解:以组件化的形式,为大量的请求或响应提供批量化处理的接口,封装着可插拔式的独立附加功能逻辑,与基本web业务逻辑功能解耦,通过hook函数能更细致的处理请求或响应过程。

django的中间件有如下特点:

1、每个中间件由一个类来表示

2、中间件的逻辑必须写在特定的接口中,这些接口被称为hook函数

3、中间件的执行有顺序依赖

4、hook函数的执行有规定顺序

5、中间件的启用会影响所有的请求/响应

6、中间件是可插拔式的,这意味着可以不启用任何中间件

7、中间件应该仅作为数据过滤器的角色对数据过滤、转换、清洗,对数据的业务处理应该放在视图系统中

8、如第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函数逻辑

1、process_request(self, request)

参数requestHttpRequest对象,此hook函数将会在路由分发前执行,有两类返回值:

1. return None  # 请求流程将会继续按照原计划执行,这应该是默认设置
2. return HttpResponse # 请求将会跳转到当前中间件的process_response函数处理并进入响应流程 注意:虽然return一个非None且非HttpResonse的值也会使得流程跳转到响应流程,不过并不建议这么做,因为每一个process_response函数都期望接收到一个HttpResponse对象以便做进一步的处理,而不是收到一个奇怪的字符串或者数字。 注意:进入响应流程的入口是当前中间件的process_response

2、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

3、process_template_response

view 视图函数中使用 render 渲染一个模版对象完成之后被调用,它必须返回一个render 方法执行后的response对象。

4、process_exception(self, request, exception)

当视图函数执行出错的时候,会把错误抛给此hook函数,有两类返回值:

1. return None  # 将会把错误对象exception提交给前一个中间件的process_exception处理
2. return HttpResponse # 将会跳转到最后一个中间件的process_response函数处理并进入响应流程 注意:不应该return exception 注意:进入响应流程的入口是最后一个中间件的process_response

5、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中间件和装饰器进行了转换。

图一、django中间件到装饰器的转换

图二、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

执行结果如下:

六、中间件的应用场景

中间件的启用会影响所有的请求/响应--->适用于大量请求/响应的批量化处理场景

中间件相互之间功能解耦,顺序依赖--->适合可插拔式的业务场景

中间件可以介入请求/响应流程--->适用于需要更加细致化处理请求/响应流程的业务场景

1、流量统计

2、恶意ip过滤

3、用户区分

4、缓存CDN

5、URL过滤

6、数据预处理

......

七、内置中间件

django框架内置了7个中间件,用于提供基本的http请求和响应处理,内置中间件的基本学习可以参考:

https://www.jb51.net/article/69953.htm

八、总结

1、装饰器和中间件都实现了装饰模式,此模式的目的是为了在不修改原有模块的条件下新增功能代码,并可以提供可插拔的效果,同时新增代码和原有代码功能上解耦。

2、类比学习很重要,可以同时提升对两个同类知识的理解。

3、中间件的角色应该是数据清洗/过滤/转换器,不应该在中间件上处理业务逻辑,而只是处理数据约束,具体的业务逻辑应该放置在视图函数中,这也是它的本职工作。

4、不要滥用中间件,过多的中间件会增加请求/响应流程的环节数,发生错误的时候提升排错难度。中间件的使用应该依赖业务场景,在最合适的地方使用最合适的技术,才能发挥最高的效率。

django框架--中间件系统的更多相关文章

  1. Django框架----中间件

    我们已经会了给视图函数加装饰器来判断是用户是否登录,把没有登录的用户请求跳转到登录页面.我们通过给几个特定视图函数加装饰器实现了这个需求.但是以后添加的视图函数可能也需要加上装饰器,这样是不是稍微有点 ...

  2. Django框架----路由系统(详细)

    Django的路由系统 Django 1.11版本 URLConf官方文档 URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表. ...

  3. django框架--路由系统

    目录 一.路由系统理解 二.路由系统功能划分 三.路由表创建 创建工具 二级路由 路由别名 动态路由及重定向 四.自定义错误页面 五.图示路由系统在框架中的定位 六.路由系统的进阶想法 一.路由系统理 ...

  4. Django框架-模板系统

    来看一段代码 def current_datetime(request): now = datetime.datetime.now() html = "<html><bod ...

  5. python 之 Django框架(路由系统、include、命名URL和URL反向解析、命名空间模式)

    12.36 Django的路由系统 基本格式: from django.conf.urls import url urlpatterns = [ url(正则表达式, views视图函数,参数,别名) ...

  6. django框架--视图系统

    目录 一.视图函数的理解 二.视图函数的定位 三.请求对象HttpRequest 四.响应对象HttpResponse 一.视图函数的理解 视图函数的作用是,对指定的url执行业务逻辑,视图函数将会作 ...

  7. Django框架----路由系统、视图和模板(简单介绍)

    一.路由配置系统(urls) URL配置(URLconf)就像Django所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表: 你就是以这种方式告诉Django,对于这个URL ...

  8. python 之 Django框架(模板系统、过滤器、simple_tag、inclusion_tag、Tags、母版、组件)

    12.35 Django模板系统 {{ }}和 {% %},变量相关的用{{}},逻辑相关的用{%%} app02/views: # 模板语言测试函数 def template_test(reques ...

  9. Django框架详细介绍---中间件(认证)

    一.绪论 在cookie和session的应用中,通过在视图函数内添加装饰器判断用户是否登录,把没有登录的用户请求跳转到登录页面,通过给几个特定视图函数加装饰器实现了这个需求.但是以后添加的视图函数可 ...

随机推荐

  1. Caused by: Unable to load configuration. - action - file:/C:/apache-tomcat-7.0.70/webapps/Structs/WEB-INF/classes/struts.xml:7:72 at com.opensymphony.xwork2.config.ConfigurationManager.getConfigurati

    Unable to load configuration. - action - file:/C:/apache-tomcat-7.0.70/webapps/Structs/WEB-INF/class ...

  2. 记一次项目使用webuploader爬坑之旅

       因前端页面开发使用的为VUE开发,又要支持IE9,遂只有基于webuploader封装一个上传组件.地址:https://github.com/z719725611/vue-upload-web ...

  3. Cache Algorithms

    1. 平均内存引用时间 T = average memory reference time m = miss ratio = 1 - (hit ratio) Tm = time to make a m ...

  4. WPF MediaKit的一点问题

    原版WPF MediaKit在捕获摄像头视频时,如果不使用640*480分分辨率输出,会出现NewVideoSample事件不被触发的问题. 经数日摸索,终于明白SetVideoCapturePara ...

  5. Java中JNI的使用详解第一篇:HelloWorld

    转自: http://blog.csdn.net/jiangwei0910410003/article/details/17465085 今天开始研究JNI技术,首先还是老套路,输出一个HelloWo ...

  6. 老刘 Yii2 源码学习笔记之 Action 类

    Action 的概述 InlineAction 就是内联动作,所谓的内联动作就是放到controller 里面的 actionXXX 这种 Action.customAction 就是独立动作,就是直 ...

  7. springmvc 孔浩

    modelAttribute属性指定该form绑定的是哪个Model,当指定了对应的Model后就可以在form标签内部其 它表单标签上通过为path指定Model属性的名称来绑定Model中的数据了 ...

  8. 层层递进Struts1(三)之Struts组成

    这篇博客我们来说一下Struts的主要组成我们,通过前几篇博客,我们知道这个框架最重要的几个步骤:获取路径.封装表单.获取转向列表.转向逻辑处理.转向,与此对应的是:ActionServlet.Act ...

  9. 切勿用普通for循环遍历LinkedList

    ArrayList与LinkedList的普通for循环遍历 对于大部分Java程序员朋友们来说,可能平时使用得最多的List就是ArrayList,对于ArrayList的遍历,一般用如下写法: p ...

  10. SSH中设置字符编码防止乱码

    1.在web.xml中加入一个过滤器和过滤范围的配置 <filter><filter-name>encoding</filter-name><filter-c ...