django 中间件

我们在前面学cookie的时候,学会了用装饰器来判定用户是否登录,如果已经登录了就可以访问页面,否则直接跳转到登录的页面。如果视图函数少的话这样是没什么问题的,可是如果有大量的视图,每个函数前都要加一个这种的装饰器,显然是不方便的,所以Django就为我们提供了一个个更加合适的方法来实现这种功能。

什么是中间件?

全局范围内改变Django的输入和输出。

但是由于作用域是全局,一定要谨慎使用,否则会影响性能。

直白一点来说,中间件是帮我们在视图函数执行前和执行后做的一些额外的操作,因为它本质上就是一个自定义类,类里定义了几个方法,Django框架会在请求的特定时间中去执行这些方法。看看下面的图

如何把 中间件做成 serverless 中间件的使用_django

 中间件的位置就决定了他的功能。

中间件的使用

 我们看一下Django项目中的settings.py这个文件,里面就有一个专门设置中间件的部分

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

其实我们做的想第一个项目,就对这个中间件进行了操作,把csrf那个类直接注释掉了。

我们看一看这个中间件的源代码(导入以后在ctrl点击去)

from django.middleware.csrf import CsrfViewMiddleware

如何把 中间件做成 serverless 中间件的使用_django_02

 

 把另外几个也看一下,就会发现其实中间件其实的作用不外乎几种(除了下划线开头的内部方法)

五种中间件的固定方法

  1. process_request
  2. process_view
  3. process_response
  4. process_exception
  5. process_template

前面三种是我们常用的,着重要理解的是前两个。

上面所有的方法返回值不过两种,一种是None,还有一种是HttpResponse。所以中间件的执行是有顺序的,设置文件中中间件的定义是一个列表,所以执行的顺序就是从上到下依次执行。如果第一个方法没有返回值,就按照列表的顺序往下走,否则就直接把HTTPResponse对象返回给用户。我们下面用一个最简单的自定义中间件来说明中间件的效果

自定义中间件的使用

定义一个最简单的视图函数,能返回个HttpResponse就可以,连模板都不用了

def test(request):
    print('in views.test')
    return HttpResponse(11111)

新建一个py文件,路径跟manage.py同级,然后声明两个类,py文件的文件名就叫my_middleware.py

"""
文件名my_middleware.py
自定义中间件
"""

from django.utils.deprecation import MiddlewareMixin

class MiddleWare_A(MiddlewareMixin):
    def process_request(self,request):
        print('这是中间件A')



class MiddleWare_B(MiddlewareMixin):
    def process_request(self,request):
        print('这是中间件B')

下面就是中间件的导入(这种用字符串导入包的方式点击这里查看)

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',
    'my_middleware.MiddleWare_A',
    'my_middleware.MiddleWare_B'
]

注意最后两行就是我们追加的自定义中间件,在访问页面的时候,就会打印出来下面的字符串

如何把 中间件做成 serverless 中间件的使用_请求处理_03

 因为我们先导入的是中间件A,所以就先执行A里的指令,反之也是成立的。在中间件执行完了以后才去执行视图里映射的函数。(因为是request请求处理,如果是response响应处理就是应该先执行视图里的函数。)

 而我们定义的函数是没有返回值的,所以返回值就默认为None,会继续执行下一个中间件。如果我们把MiddleWare_A修改一下,会有什么样的效果呢?

class MiddleWare_A(MiddlewareMixin):
    def process_request(self,request):
        print('这是中间件A')
        return HttpResponse('中间件A返回值')

再访问/test/,页面就显示了中间件的返回内容

如何把 中间件做成 serverless 中间件的使用_中间件_04

 

 后台打印的数据也发生了变化

如何把 中间件做成 serverless 中间件的使用_中间件_05

 

 就连视图里的函数都没有执行。

请求处理和响应处理

 在上面一节我们用一个最简单的案例说明了中间件的原理和效,在5种中间件的使用中,最重要的就是请求处理和响应处理,我们这里看一下具体的使用注意事项

请求处理

请求处理:process_response(self,request)

通过前面的演示已经很清楚了,有以下几点:

  • 执行顺序:按照注册的顺序(settings.py中从上到下的顺序)
  • 返回值:无返回值或返回值为None,继续只写那个后续中间件中的process_request方法

 

响应处理

响应处理:process_response(self,request,response)

把前面定义的A和B的中间件修改一下,增加一个响应处理,做一下测试

class MiddleWare_A(MiddlewareMixin):
    def process_request(self,request):
        print('这是中间件A的请求处理')

    def process_response(self,request,response):
        print('中间件A里的响应处理')
        return response

class MiddleWare_B(MiddlewareMixin):
    def process_request(self,request):
        print('这是中间件B的请求处理')

    def process_response(self,request,response):
        print('中间件B里的响应处理')
        return response

注册的方式不变,先A后B,然后请求一下页面

就会有下面的效果

如何把 中间件做成 serverless 中间件的使用_请求处理_06

 

 

如何把 中间件做成 serverless 中间件的使用_django_07

 从运行的效果可以看出来,响应处理是在视图函数执行完毕以后才运行的,并且运行的顺序刚好和注册的顺序相反,是从列表最后向前面执行。

并且和请求处理不同,响应处理必须有一个response类的返回值,整体的效果是这样的

如何把 中间件做成 serverless 中间件的使用_中间件_08

 

 

视图请求

 视图请求:process_view(self,request,view_func,view_args,view_kwargs)参数含义是这样的

def process_view(self,request,view_func,view_args,view_kwargs):
    """
    :param request:浏览器发送的请求对象
    :param view_func:要执行的视图函数
    :param view_args:要执行的视图函数的位置参数
    :param view_kwargs:要执行的视图函数的关键字参数

    """
    pass

 和前面的process_response一样,我们把中间件A和B都添加一个process_view,运行一下看看效果

class MiddleWare_A(MiddlewareMixin):
    def process_request(self,request):
        print('这是中间件A的请求处理')

    def process_response(self,request,response):
        print('中间件A里的响应处理')
        return response

    def process_view(self,request,view_func,view_args,view_kwargs):
        """
        :param request:浏览器发送的请求对象
        :param view_func:要执行的视图函数
        :param view_args:要执行的视图函数的位置参数
        :param view_kwargs:要执行的视图函数的关键字参数

        """
        print('中间件A里的process_view')
        print('A_view_func:——————',view_func,type(view_func))

class MiddleWare_B(MiddlewareMixin):
    def process_request(self,request):
        print('这是中间件B的请求处理')

    def process_response(self,request,response):
        print('中间件B里的响应处理')
        return response

    def process_view(self,request,view_func,view_args,view_kwargs):
        print('中间件B中的process_view')
        print('B_view_func:——————',view_func)

修改后的my_middleware.py

访问一下,看看效果

如何把 中间件做成 serverless 中间件的使用_django_09

 

 所以,process_view的执行顺序是按照注册的顺序从前到后,执行的时间是在路由找到映射的关系以后,但是在执行视图函数前。

返回值是None时,会继续执行后续的中间件中的process_view方法。如果返回response对象就会跳过后面的process_view方法

但是有一点要注意:process_view的返回值和process_response是没关系的,如果中间件存在process_response,最终的返回值是由process_response决定的。整个流程如下图所示

如何把 中间件做成 serverless 中间件的使用_请求处理_10

 

也就是说不管process_request或process_view之间只要任何一个有返回的HttpResponse,视图函数都是不会执行的

而浏览器最终的渲染内容是由最后的process_response决定的。

异常处理和模板响应处理

 前面三种处理是最常见的后面还有两种处理方法不太常用,我们大概了解就行了。

异常处理

异常处理:process_exception(self,request,exception),当视图函数有异常抛出的时候才会执行

执行顺序:按照注册时的倒叙执行

执行时间:在视图函数抛出异常时执行

返回值:None或HttpResponse,效果和process_request的返回值一样。

模板响应处理

模板响应处理process_template_response(self,request,response)用的就非常少了

它的参数是一个HttpRequest对象,response是有视图或者中间件产生的TemplateResponse对象,也就是说它只能在视图函数执行完毕后才执行(视图必须返回一个render()方法,或者该对象是一个TemplateResponse对象或等价方法)

 这个使用的环境太少,就不讲了,如果需要用的时候再查查吧!

中间件的使用案例

结合两个案例来看一下中间件的使用

url白名单

白名单的实现比较简单。假设我们需要用一个白名单来限制被请求的url,通过中间件的功能实现,就把中间件写到前面的my_middleware.py文件中,新建一个列表,里面放上我们允许访问的url,如果请求访问的url不在列表内,就返回一个response,不执行后面的内容了。

URLs = ['/test/']       #白名单列表


#白名单处理
class WhiteList(MiddlewareMixin):
    def process_request(self,request):
        url = request.path_info     #获取请求地址(不包含参数)
        if url in URLs:
            pass
        else:    
            return HttpResponse('无权访问该地址')

这样就行了,是不是很简单,就不用在视图中对url进行判定了。在每次request的时候直接处理。

登录状态判定

在这一章一开始的时候就提到过,以前用装饰器的方式来判定登录状态需要对每个视图函数进行装饰,太麻烦了,所以就可以利用这个中间件的形式来操作。

但是这里有个注意点:在注册的时候,因为我们的登录状态要用到session,所以一定要把自定义的中间件放在session的中间件的后面。

中间件的定义也很简单,直接获取session的状态就可以

class Login_Check(MiddlewareMixin):

    def process_request(self,request):
        log_stat = request.session.get('is_log')
        if log_stat == 1:
            pass
        return HttpResponse('请登录')

实际的使用环境应该是如果没有登录状态时直接重定向到登录页面。并且在试的时候要加上一个添加session的过程,除非再添加一个session,总起来用法是这样的

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

class Check(MiddlewareMixin):
    def process_request(self,request):
        if 'login' in request.path:
            return None
        log_stat = request.session.get('is_log')
        if log_stat == 1:
            return None
        return redirect('/session_test/login')

my_middleware.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'my_middleware.Check',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

setting.py部分

在中间件中,加上了对url的判断,就把login的页面排除掉了,否则就会一致重定向。