Django--中间件

什么是中间件

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

Django中间件的定义:

Middleware is a framework of hooks into Django’s request/response processing. 
It’s a light, low-level “plugin” system for globally altering Django’s input or output.

中间件的作用

如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。

可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。

Django默认的中间件:(在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下图)

[复制代码](javascript:void(0)😉

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',
]

每一个中间件都有具体的功能

自定义中间件

中间件中主要有以下几个方法:

  1. process_request(self,request)
  2. process_view(self, request, callback, callback_args, callback_kwargs)
  3. process_template_response(self,request,response)
  4. process_exception(self, request, exception)
  5. process_response(self, request, response)

process_request/process_response

当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求时process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。

上述截图中的中间件都是django中的,我们也可以自己定义一个中间件,我们可以自己写一个类,但是必须继承MiddlewareMixin

# 第一步,导入
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


# 第二步,自定义中间件
class Md1(MiddlewareMixin):
    def process_request(self,request):
        print("Md1请求")
        #return HttpResponse("Md1中断")
 
    def process_response(self,request,response):
        print("Md1返回")
        return response

class Md2(MiddlewareMixin):
    def process_request(self,request):
        print("Md2请求")
        
    def process_response(self,request,response):
        print("Md2返回")
        return response
    
    
# 第三步,在views.py中定义一个测试函数
def index(request):
    print("view函数...")
    return HttpResponse("OK")

# 第四步,在settings.py的MIDDLEWARE里注册自己定义的中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    ....
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'MyMiddlewares.Md1',
    'MyMiddlewares.Md2',
]


# 结果
"""
Md1请求
Md2请求
view函数...
Md2返回
Md1返回
"""


"""
注意:如果当请求到达请求1的时候直接不符合条件返回,即return HttpResponse("Md1中断"),程序将把请求直接发给中间件1返回,然后依次返回到请求者,结果如下:

返回Md1中断的页面,后台打印如下:

Md1请求
Md1返回
"""

执行流程如下:

  • 请求到达中间件之后,先按照正序执行每个注册中间件的process_request方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法(注意不是掉头执行所有的process_response方法),将HttpResponse对象返回给浏览器。也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法。

process_view

process_view(self, request, view_func, view_args, view_kwargs)

该方法有四个参数

  • request是HttpRequest对象。
  • view_func是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)
  • view_args是将传递给视图的位置参数的列表.
  • view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。

Django会在调用视图函数之前调用process_view方法。

它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,那么将不会执行Django的视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):
    def process_request(self,request):
        print("Md1请求")
        #return HttpResponse("Md1中断")
        
    def process_response(self,request,response):
        print("Md1返回")
        return response

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

class Md2(MiddlewareMixin):
    def process_request(self,request):
        print("Md2请求")
        return HttpResponse("Md2中断")
    
    def process_response(self,request,response):
        print("Md2返回")
        return response

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

执行结果如下:

"""
Md1请求
Md2请求
Md1view
Md2view
view函数...
Md2返回
Md1返回
"""

执行流程如下:

  • process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。假如中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行。

process_exception

process_exception(self, request, exception)

该方法两个参数:

  • 一个HttpRequest对象

  • 一个exception是视图函数异常产生的Exception对象。

这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

示例修改如下:

class Md1(MiddlewareMixin):
    def process_request(self,request):
        print("Md1请求")
        #return HttpResponse("Md1中断")
        
    def process_response(self,request,response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        # return HttpResponse("hello")
        # response=callback(request,*callback_args,**callback_kwargs)
        # return response
        print("md1 process_view...")

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

        
class Md2(MiddlewareMixin):
    def process_request(self,request):
        print("Md2请求")
        # return HttpResponse("Md2中断")
        
    def process_response(self,request,response):
        print("Md2返回")
        return response
    
    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("md2 process_view...")

    def process_exception(self,request,exception):
        print("md1 process_exception...")
        
        
# 运行结果
"""
Md1请求
Md2请求
md1 process_view...
md2 process_view...
view函数...

Md2返回
Md1返回
"""

流程图如下:

process_template_response

该方法对视图函数返回值有要求,必须是一个含有render方法类的对象,才会执行此方法

class Test:
    def __init__(self,status,msg):
        self.status=status
        self.msg=msg
    def render(self):
        import json
        dic={'status':self.status,'msg':self.msg}

        return HttpResponse(json.dumps(dic))
def index(response):
    return Test(True,'测试')

中间件的应用场景

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

  2. URL访问过滤:如果用户访问的是login视图(放过)

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

posted @ 2019-08-23 16:36  蔚蓝的爱  阅读(129)  评论(0编辑  收藏  举报