zoukankan      html  css  js  c++  java
  • django的中间件:process_request|process_response|process_view|process_exception





    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', # 定义了2个自定义的中间件 'middle.md.middle_first', 'middle.md.middle_second' ]

      

    #Auther Bob
    #--*--conding:utf-8 --*--
    
    from django.utils.deprecation import MiddlewareMixin
    import time
    from django.shortcuts import HttpResponse
    
    # 中间件就是一个类,下面我们自定义了2个中间件,中间件又叫做管道
    
    class middle_first(MiddlewareMixin):
        def process_request(self,request):
            print("middle_first--process_request",time.time())
            return HttpResponse("大爷慢走")
        def process_response(self,request,response):
            print("middle_first--process_response", time.time())
            print(response, type(response))
            return response
    
    class middle_second(MiddlewareMixin):
        def process_request(self,request):
            print("middle_second--process_request",time.time())
    
    
        def process_response(self,request,response):
            print("middle_second--process_response", time.time())
            print(response,type(response))
            return response
    

      

    中间件函数的参数是固定的

    先看process_request方法,参数有一个request

      def process_request(self,request):
            path = request.path_info
            # l = path.split("/")
            # if l[2] in black_list:
            #     return HttpResponse("不能访问")
            re_compile_obj = re.compile("/")
    
            l = re_compile_obj.split(path)
            for i in l:
                if i in black_list:
                    return HttpResponse("不能访问")
    

      

    在看process_response方法,参数必须要要有个response,这个参数必须要有,而且这个process_response的函数必须要有返回值,如果没有,django会把报错的,最后一个中间件的process_response把返回值返回给倒数第一个中间件的process_response函数,然后在返回倒数第三个中间件的process_response函数,这个参数默认就是视图函数的返回,我们也可以自定义返回值,下面的例子就是我们自定义返回值,process_response是按照注册顺序的倒序执行

        def process_response(self,request,response):
            print("--------->","这个是response的中间件")
    
            return HttpResponse("中间件直接返回的")
    

     

    结果如下

     我们还使用默认的response的返回值,也就是用视图函数的返回值

        def process_response(self,request,response):
            print("--------->","这个是response的中间件")
    
            # return HttpResponse("中间件直接返回的")
            return response
    

      

    结果如下

     process_view函数,多个中间件的process_view函数是按照注册的正序执行,在执行视图函数之前执行,如果有返回值,则不会执行视图函数的方法,执行返回执行process_response函数,如果没有返回值,则会执行完process_view后在执行视图函数

        def process_view(self,request,view_func,view_args,view_kwargs):
            """
    
            :param request: 请求对象
            :param view_func: 将要执行的视图函数
            :param view_args:将要执行的函数的位置参数
            :param view_kwargs:将要执行的函数的关键字参数
            :return:
            """
    
            print("process_view-------->")
            return HttpResponse("process_view返回值")
    

      

     上面的例子的process_view函数有返回值,我们看下结果,可以看到返回的process_view函数的返回值,没有去执行视图函数

     下面这个例子,process_view函数中没有返回值

        def process_view(self,request,view_func,view_args,view_kwargs):
            """
    
            :param request: 请求对象
            :param view_func: 将要执行的视图函数
            :param view_args:将要执行的函数的位置参数
            :param view_kwargs:将要执行的函数的关键字参数
            :return:
            """
    
            print("process_view-------->")
            # return HttpResponse("process_view返回值")
    

      

    结果如下,我们看到返回的是视图的返回值

    在看下process_exception的中间件的函数

        def process_exception(self,request,exception):
            """
            
            :param request: 这个是请求对象
            :param exception: 这个是视图函数返回的错误
            :return: 
            """
            print(exception)
            print("这个是中间件exception函数")
    
            return redirect("https://www.baidu.com")
    

      

    他的执行顺序是按照在中间件注册的倒序执行的,如果视图函出错,才会执行,下面我们模拟视图函数出错,用raise方法主动抛出错误

    def test_forms(request):
        print(request.path_info)
        # for i in range(10):
        #     models.City.objects.create(
        #         name = "北京" + str(i)
        #     )
        print(models.City.objects.all().values("name"))
    
        if request.method.lower() == "post":
            myform = Myform(request.POST)
            # 把post方式传递过来的数据传递个myform对象
    
    
            if myform.is_valid():
                # 对前端发过来的数据做校验,如果合法的话
                name = myform.cleaned_data.get("name")
                pwd = myform.cleaned_data.get("pwd")
                rep_pwd = myform.cleaned_data.get("rep_pwd")
                print(myform.cleaned_data)
    
            else:
                pass
        else:
            myform = Myform()
        raise ValueError("视图函数执行失败了")
    
        return render(request,"form1.html",{"myform":myform})
    

      

    看下打印的信息

    视图函数执行失败了
    这个是中间件exception函数
    ---------> 这个是response的中间件

    先看下中间件在django整个流程处于哪个阶段

     中间件的执行顺序是这样的

    按照settings里面MIDDLEWARE的列表中的顺序来执行,先执行第一个中间件的process_request方法,然后执行第二个中间件的process_request方法,直到最后一个中间件的process_request方法,执行完成之后,就到views中的视图函数,然后返回的时候在从最后一个中间件的process_response方法,然后执行倒数第二个process_response方法,一直都第一个中间件的process_response方法

    这里我们需要注意,中间件的函数中的request参数,和视图函数中的request参数是一样的,response参数,就是视图函数中return返回的值

    我们先看下执行顺序

    首先我们在middleware中定义的中间件的顺序是这样的

        # 定义了2个自定义的中间件
        'middle.md.test',
        'middle.md.middle_first',
        'middle.md.middle_second'
    

      

    我们在看先我们的中间件函数

    from django.utils.deprecation import MiddlewareMixin
    import time
    from django.shortcuts import HttpResponse
    
    # 中间件就是一个类,下面我们自定义了2个中间件,中间件又叫做管道
    
    class test(MiddlewareMixin):
        def process_request(self,request):
            print("test--process_request", time.time())
    
        def process_response(self,request,response):
            print("test--process_response", time.time())
            return response
    
    
    class middle_first(MiddlewareMixin):
        def process_request(self,request):
            print("middle_first--process_request",time.time())
            # return HttpResponse("大爷慢走")
        def process_response(self,request,response):
            print("middle_first--process_response", time.time())
            # print(response, type(response))
            return response
    
    class middle_second(MiddlewareMixin):
        def process_request(self,request):
            print("middle_second--process_request",time.time())
    
    
        def process_response(self,request,response):
            print("middle_second--process_response", time.time())
            # print(response,type(response))
            return response
    

      

    这里我们要注意,在我们定义的3个中间件的类中,process_request方法中都没有return,我们从客户端发起请求,看下中间件函数的执行顺序,和我们上面分析的是一样的

     下面我们在在middle_first类中的process_request方法中定义一个return,在看下执行顺序

     我们再次通过客户端去访问

    我们在看下执行顺序

     先执行test的process_request方法,然后执行middle_first的process_request方法,因为这里的方法中我们有定义return,所以就不会在往下执行,直接走middle_first的process_response方法,然后执行test的process_response方法

    现在我们对中间件的执行顺序应该已经比较清楚了吧

    下面我们通过源代码来分析一下中间件

    首先我们先看下类中定义的__call__方法,这个方法什么时候会执行呢?

    前面我忘记了,后来测试了一下,现在知道了,我们在回忆一下

    class test_call(object):
        def __init__(self,call):
            self.call = call
    
        def __call__(self, *args, **kwargs):
            print("现在是执行{call}".format(call = self.call))
    
    
    if __name__ == '__main__':
        t = test_call("cui")
    
        t()
    

      

    执行结果

     所以__call__方法是在执行类的对象的时候会调用__call__方法

    我们看到我们定义的中间件的类都继承了MiddlewareMixin这个类,我们来看下这个类是怎么写的

    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
    

      

    我们重点看下看这里

    首先定义response为空,因为中间件的执行顺序是先执行process_request方法,我们这里通过反射的hasattr方法,先来判断是否有定义process_request方法,如果有定义,则执行,执行process_request方法,如果这个方法有返回值,那么就不会执行第二个if中的代码,第二个if中的代码我们后面在说他的意思,如果,有process_request方法,且有返回值,则会执行第三个if中的方法,就会执行这个中间的process_response方法,这样就和我们上面的例子对应上了;

    下面我们来说一下第二个if中的代码的作用,他的作用就是执行下一个中间件函数的__call__方法,如果没有下一个中间件,则会执行视图函数中对应的方法

    我们推荐大家这样写,自己实现写middlewareMixin类,然后我们自己的中间件类继承我们自己写的middlewareMixin类就可以了

    from django.utils.deprecation import MiddlewareMixin
    import time
    from django.shortcuts import HttpResponse
    
    
    class my_MiddlewareMixin(object):
        def __init__(self, get_response=None):
            self.get_response = get_response
            super(my_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
    
    
    # 中间件就是一个类,下面我们自定义了2个中间件,中间件又叫做管道
    
    class test(my_MiddlewareMixin):
        def process_request(self,request):
            print("test--process_request", time.time())
    
        def process_response(self,request,response):
            print("test--process_response", time.time())
            return response
    
    
    class middle_first(my_MiddlewareMixin):
        def process_request(self,request):
            print("middle_first--process_request",time.time())
            return HttpResponse("大爷慢走")
        def process_response(self,request,response):
            print("middle_first--process_response", time.time())
            # print(response, type(response))
            return response
    
    class middle_second(my_MiddlewareMixin):
        def process_request(self,request):
            print("middle_second--process_request",time.time())
    
    
        def process_response(self,request,response):
            print("middle_second--process_response", time.time())
            # print(response,type(response))
            return response
    

      

    class test(my_MiddlewareMixin):
        def process_request(self,request):
            print("test--process_request", time.time())
    
        def process_view(self,request,view_func,view_args,view_kwargs):
            print(view_func, view_args, view_kwargs, sep="-------->")
            print("test--process_view", time.time())
    
    
        def process_response(self,request,response):
            print("test--process_response", time.time())
            return response
    
        def process_exception(self,request,exception):
            """
    
            :param request:
            :param exception: 只能捕获视图函数中的异常捕获,对中间件中的其他函数的错误是捕获不到的,这里的excetion就是错误的信息
            :return:
            """
            print("test--process_exception", time.time())
    

      

    如果视图函数中不出错的话,process_exception是永远都不会执行的,比如下面一个正常的访问流程,我们访问的test函数没有出错,按照顺序执行,先执行第一个中间件的process_request方法,然后是第二个中间件的process_request方法,然后是第三个中间件的process_request方法,然后执行第一个中间件的process_view方法,然后是第二个中间件的process_view方法,然后是第三个中间件的process_view方法,然后执行视图函数,在返回的时候,先执行最后一个中间件的process_response方法,然后是倒数第二个process_view方法,然后是倒数第三个中间件的processs_response方法

     下面我们看下如何视图函数中有错误的话,会如何执行

    我们首先在视图函数中构建了一个错误

    def test(request):
        int("hahah")
        print("test------->views",time.time())
        return HttpResponse("last_app1")
    

      

    再次通过客户端去访问,我们在第二个中间件的process_exception方法中做了return返回

    执行顺序是下面这样的

    先执行第一个中间件的process_request方法,然后是第二个中间件的process_request方法,最后是第三个中间件的process_request方法,然后执行第一个中间件的process_view方法,然后是第二个中间件的process_view方法,最后是执行第三个中间件的process_view方法,这里很关键,因为我们执行视图函数报错,因为我们人为构建了一个错误,开始执行最后一个中间件的process_exception方法,执行完成后,执行倒数第二个中间件的process_exception方法,执行到这里,这个函数有个return返回值,那么就不会在去执行第一个中间件的process_exception的方法了,这个时候就开始执行最后一个中间件的process_response方法,然后是倒数第二个中间件的process_exception方法,最后是倒数第三个中间件的process_response方法

    补一个中间件的小例子,就是黑名单的意思,如果访问的是某个url则直接给返回

     下面看下代码

    from django.middleware.security import SecurityMiddleware
    from django.utils.deprecation import MiddlewareMixin
    import re
    from django.shortcuts import HttpResponse
    from django.shortcuts import render
    from django.shortcuts import redirect
    
    black_list = ["publish","add_publish"]
    
    class My_first_mid(MiddlewareMixin):
        def process_request(self,request):
            path = request.path_info
            # l = path.split("/")
            # if l[2] in black_list:
            #     return HttpResponse("不能访问")
            re_compile_obj = re.compile("/")
    
            l = re_compile_obj.split(path)
            for i in l:
                if i in black_list:
                    return HttpResponse("不能访问")
    

      

    前端访问的效果

    首先是可以访问

    然后是不能访问

     

    经过上面的学习,我们做下总结

    中间件的定义:
    wsgi之后 urls.py之前 在全局 操作Django请求和响应的模块!

    中间件的使用:
    5个固定的方法
    process_request(self, request)
    执行顺序:
    按照注册的顺序(在settings.py里面设置中 从上到下的顺序)
    何时执行:
    请求从wsgi拿到之后
    返回值:
    返回None,继续执行后续的中间件的process_request方法
    返回response , 不执行后续的中间件的process_request方法

    process_response
    执行顺序:
    按照注册顺序的倒序(在settings.py里面设置中 从下到上的顺序)
    何时执行:
    请求有响应的时候
    返回值:
    必须返回一个response对象

    process_view(self, request, view_func, view_args, view_kwargs):
    执行顺序:
    按照注册的顺序(在settings.py里面设置中 从上到下的顺序)
    何时执行:
    在urls.py中找到对应关系之后 在执行真正的视图函数之前
    返回值:
    返回None,继续执行后续的中间件的process_view方法
    返回response,


    process_exception(self, request, exception)
    执行顺序:
    按照注册顺序的倒序(在settings.py里面设置中 从下到上的顺序)
    何时执行:
    视图函数中抛出异常的时候才执行
    返回值:
    返回None,继续执行后续中间件的process_exception
    返回response,



    process_template_response(self, request, response)
    执行顺序:
    按照注册顺序的倒序(在settings.py里面设置中 从下到上的顺序)
    何时执行:
    视图函数执行完,在执行视图函数返回的响应对象的render方法之前
    返回值:
    返回None,继续执行后续中间件的process_exception
    返回response,


    Django调用 注册的中间件里面五个方法的顺序:
    1. process_request
    urls.py
    2. process_view
    view
    3. 有异常就执行 process_exception
    4. 如果视图函数返回的响应对象有render方法,就执行process_template_response
    5. process_response

  • 相关阅读:
    Oracle、Db2、SqlServer、MySQL 数据库插入当前系统时间
    Mybatis---在控制台打印sql语句
    多线程实现的四种方法
    Restful架构
    maven中的pom配置文件一——spring,mybatis,oracle,jstl,json,文件上传
    spring mvc配置
    spring的事务
    cglib动态代理
    jdk动态代理
    spring总结————AOP面向切面总结
  • 原文地址:https://www.cnblogs.com/bainianminguo/p/9440610.html
Copyright © 2011-2022 走看看