• 忠诚的皇家守卫团——中间件


    一、什么是中间件呢?
    二、中间件的五员大将
    三、中间件的执行流程
     
     
     
    一、什么是中间件呢?

    枯燥的中间件概念
    按照官方的说法,中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局变量中改变Django的输入和输入。每个中间件组件都负责做一些特定的功能。但由于其影响的是全局,因此要谨慎使用。
    那到底什么是中间件呢?中间件其实就是帮助我们在Django的运行过程中的某个环节来执行特定的操作,比如在视图函数执行之后response一个特定的内容。它的本质是定义了一个类,类中定义了一些方法,Django框架会在请求的特定的时间去执行这些方法。
     
    emmm...看不懂,让我们来从源码中找到答案吧!
    1.让我们先来看一下一直在默默付出的Django自带的中间件(setting.py中的MIDDLEWARE配置项中)
    # 中间件相关的都配置在这里
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',    # session相关的中间件
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',    # csrf相关的中间件
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
     
    2.既然Django自带有中间件,那么我们不妨去观摩一下它们的本质(源码)吧!
    # 查看中间件源码的方法:
    # 1.先写from A import B
    # 2.按住Ctrl,鼠标同时点击B
    from django.middleware.security import SecurityMiddleware
    from django.contrib.sessions.middleware import SessionMiddleware
    from django.middleware.common import CommonMiddleware
    from django.middleware.csrf import CsrfViewMiddleware
     
    3.以下是它们的大致内容:
    # 由于源码太多,我们只截取大致框架,具体我们之后再讨论
    django.middleware.security.SecurityMiddleware 的源码
     
    class SecurityMiddleware(MiddlewareMixin):
        def __init__(self, get_response=None):    # 初始化
            pass
        def process_request(self, request):    # 处理请求的实例方法
            pass
        def process_response(self, request, response):    #处理响应的实例方法
            pass
    django.contrib.sessions.middleware.SessionMiddleware 的源码
     
    class SessionMiddleware(MiddlewareMixin):    # 定义了一个类,继承MiddlewareMixin
        def __init__(self, get_response=None):    # 初始化
            pass
        def process_request(self, request):    # 处理请求的实例方法
            pass
        def process_response(self, request, response):    # 处理响应的实例方法
            pass
    django.middleware.csrf.CsrfViewMiddleware 的源码
     
    class CsrfViewMiddleware(MiddlewareMixin):    # 定义了一个类,继承MiddlewareMixin
        def _accept(self, request):    # 私有方法
            pass
        def _reject(self, request, reason):    # 私有方法
            pass
        def _get_token(self, request):    # 私有方法
            pass
        def _set_token(self, request, response):    # 私有方法
            pass
        def process_request(self, request):    # 处理请求的实例方法
            pass
        def process_view(self, request, callback, callback_args, callback_kwargs):    # 处理视图的实例方法
            pass
        def process_response(self, request, response):    # 处理响应的实例方法
            pass
     
    4.通过对Django自带的几个中间件源码的大致分析,我们可以发现,其实中间件就是定义了一个类,类中写了几个方法:比如处理响应的、处理视图的、处理请求的..这些东西好像很固定?!好吧,接下来让我们来具体分析一下它的实例方法!
     
     
     
    二、中间件的五员大将

    中间件的五员大将,分别是:(主要的是process_request和process_response)
    process_request(self,request)    # 处理请求的
    process_view(self, request, view_func, view_args, view_kwargs)    # 处理视图的
    process_template_response(self,request,response)    # 处理模板相关的
    process_exception(self, request, exception)    # 处理异常的
    process_response(self, request, response)    # 处理响应的
    而它们的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。
    第一个要了解的是两名守门大将(request 和 response)
     
    第一步:新建一个与manage.py同级别的py文件
    # mymiddleware.py
     
    from django.utils.deprecation import MiddlewareMixin    # 导入要继承的MiddlewareMixin
    from django.shortcuts import render, redirect, HttpResponse    # 导入三件套
     
    class test1(MiddlewareMixin):    #定义一个名为test1的类,继承MiddlewareMixin
        def process_request(self, request):    #处理请求的实例方法
            print('-----test1-----request-----')    #打印用来分析test1的request、response与test2的request、response的执行顺序
            return    #返回None,正常执行,如果返回的是HttpResponse,则不执行后面的内容,直接往回响应
        def process_response(self, request, response):
            print('-----test1-----response-----')
            return response    #通过分析源码得到(这里需要自己分析)返回response是正常执行,因此,只需要中间件打印,打印之后就正常执行
     
    class test2(MiddlewareMixin):    #定义一个名为test2的类,与test1做对比
        def process_request(self, request):
            print('-----test2-----request-----')
            return
        def process_response(self, request, response):
            print('-----test2-----response-----')
            return response
     
    第二步:把新写的中间件添加到Django的配置中
    MIDDLEWARE = [
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'mymiddleware.test1',    # mymiddleware.py文件下的test1类
        'mymiddleware.test2',    mymiddleware.py文件下的test2类
    ]
     
    第三步:运行server,查看结果并分析
    -----test1-----request-----
    -----test2-----request-----
    -----views-----    #这里是我在views中写了一个函数,这个函数打印-----views-----
    -----test2-----response-----
    -----test1-----response-----
     
    第四步:分析
              a. 首先运行test1类中处理request的实例方法,打印出了-----test1-----request-----
              b. 然后运行test2类中处理request的实例方法,打印出了-----test2-----request-----
              c. 接着运行到视图函数中,打印出了-----views-----
              d. 然后从视图函数中拿到response,开始往回走,由于来的时候先经过test1,在经过test2,所以在回去的时候,要先经过test2,再经过test1,因此打印出了-----test2-----response-----
              e. 走过test2的response之后,走到test1的response,打印出了-----test1-----response-----
              f.顺利走过自定义的中间件所有流程之后,拿着response通过wsgi返回给浏览器
     
    第五步:总结
              a. process_request() 只有一个参数request,这个request和视图函数中的request是一样的。它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,而将相应对象返回给浏览器。
              b. 多个中间件中的process_response() 方法是按照MIDDLEWARE中的注册顺序倒序执行的。它有两个参数,一个是request,一个是response,request是和process_request中一样的对象,response是视图函数返回的HttpResponse对象。该方法的返回值也必须是HttpResponse对象。
              c. 中间件的process_request方法是在执行视图函数之前执行的。而process_response方法是在执行视图函数之后执行的。
              d. 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值执行。process_request是从前到后依次执行的;而process_response是从后到前依次执行的。
              e. 不同中间件之间传递的request和response都是同一个对象。(因此,如果在中间件这里截取到request和response却并不把它交给下一个环节,那么后面会接收不到request和response)
     
     
    了解了两位守门大将之后,我们再来认识一下宫廷守卫(process_view())吧!
    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)。
    它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。
    第一步:写入中间件到mymiddleware中
    # mymiddleware.py
     
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import render, redirect, HttpResponse
     
    class test1(MiddlewareMixin):
        def process_request(self, request):
            print('-----test1-----request-----')
            return
        def process_response(self, request, response):
            print('-----test1-----response-----')
            return response
     
        def process_view(self, request, view_func, view_args, view_kwargs):    #处理视图的实例方法,它有五个参数
            print("-----test1-----view-----")    # 打印,用来分析执行顺序
            print(view_func, view_func.__name__)    #一个是打印参数view_func传递的是什么,另外打印它的__name__属性,也就是函数名
     
    class test2(MiddlewareMixin):
        def process_request(self, request):
            print('-----test2-----request-----')
            return
        def process_response(self, request, response):
            print('-----test2-----response-----')
            return response
     
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-----test2-----view-----")    #用来与test1中的view做对比
            print(view_func, view_func.__name__)
     
    第二步:运行server,查看结果并分析
    -----test1-----request-----
    -----test2-----request-----    # 按正序运行,先运行test1的request,再运行test2的request
    -----test1-----view-----    # test2中的request运行完成之后接着运行test1中的view!
    <function home at 0x02CCB468> home    然后发现view_func传递的是一个函数的地址,这里函数是home函数(视图中自定义的函数,也就是request和response传递的对象)
    -----test2-----view-----    # test1中的view运行结束之后,接着运行test2中的view,原来view和request一样,都是正向运行
    <function home at 0x02CCB468> home    # test1中的view和test2中的view一样,但是想一下,这个处理view实例方法是干嘛的呢?它出现在request运行之后,在真正运行视图函数之前,emmmm...我也不知道这哥们是做什么的,暂且称它为宫廷守卫吧!
    -----views-----    #这个是真正的视图函数执行的结果
    -----test2-----response-----    # 接下来response按照倒序正常运行
    -----test1-----response-----
     
    第三步:分析
              a. 首先运行test1类中处理request的实例方法,打印出了-----test1-----request-----
              b. 然后运行test2类中处理request的实例方法,打印出了-----test2-----request-----
              c. 接着运行test1类中处理view的实例方法,打印出了-----test1-----view-----       
              d. 然后运行test2类中处理view的实例方法,打印出了-----test2-----view-----
              e. 接着运行到视图函数中,打印出了-----views-----
              f. 然后从视图函数中拿到response,开始往回走,由于来的时候先经过test1,在经过test2,所以在回去的时候,要先经过test2,再经过test1,因此打印出了-----test2-----response-----
              g. 走过test2的response之后,走到test1的response,打印出了-----test1-----response-----
              h. 顺利走过自定义的中间件所有流程之后,拿着response通过wsgi返回给浏览器
     
    第四步:总结
              a. process_view方法是在process_request之后,视图函数之前执行的,执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的
              b. 它返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。
     
     
    接下来要登场的是皇家守卫中..霸气侧漏大名鼎鼎的——医疗兵(processe_exception)(:D)
    process_exception的个人简介
    process_exception(self, request, exception)
    request是HttpRequest对象。
    exception是视图函数异常产生的Exception对象。
    这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
    第一步:写入中间件到mymiddleware中
    # mymiddleware.py
     
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import render, redirect, HttpResponse
     
    class test1(MiddlewareMixin):
        def process_request(self, request):
            print('-----test1-----request-----')
            return
        def process_response(self, request, response):
            print('-----test1-----response-----')
            return response
     
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-----test1-----view-----")
            print(view_func, view_func.__name__)
     
        def process_exception(self, request, exception):    # 处理异常的实例方法,有两个参数
            print(exception)    # 打印exception这个参数,猜测是异常信息
            print("-----test1-----exception-----")    # 打印,分析运行顺序
            return HttpResponse(str(exception))    #由于抛出自定义异常会报错,我们在这里把异常截获并改变response
     
    class test2(MiddlewareMixin):
        def process_request(self, request):
            print('-----test2-----request-----')
            return
        def process_response(self, request, response):
            print('-----test2-----response-----')
            return response
     
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-----test2-----view-----")
            print(view_func, view_func.__name__)
     
        def process_exception(self, request, exception):    
            print(exception)    # 用来分析是否与test1中是同一个异常
            print("-----test2-----exception-----")    # 用来与test1做对比
     
    第二步:由于process_exception只有当遇到视图函数中抛出异常时才做处理,因此我们手动抛出一个异常
    # views.py
     
    from django.shortcuts import render, redirect, HttpResponse
     
    # Create your views here.
    def home(request):
        print('-----views-----')
        raise ValueError("自定义异常")    #抛出自定义异常,ValueError
        return HttpResponse('-----home-----')
     
    第三步:运行server,查看结果并分析
    -----test1-----request-----
    -----test2-----request-----
    -----test1-----view-----
    <function home at 0x02CCB468> home
    -----test2-----view-----
    <function home at 0x02CCB468> home
    -----views-----    # 正常运行到视图函数中打印的部分
    自定义异常    # 参数传递的是自定义异常
    -----test2-----exception-----    # 可以看到先运行了test2中的exception
    自定义异常    # test1和test2中的异常是一个异常,可以推断出,test2中捕获异常之后,把异常传递给了test1
    -----test1-----exception-----    # process_exctption是倒序运行
    -----test2-----response-----
    -----test1-----response-----
     
    第四步:分析
              a. 首先运行test1类中处理request的实例方法,打印出了-----test1-----request-----
              b. 然后运行test2类中处理request的实例方法,打印出了-----test2-----request-----
              c. 接着运行test1类中处理view的实例方法,打印出了-----test1-----view-----       
              d. 然后运行test2类中处理view的实例方法,打印出了-----test2-----view-----
              e. 接着运行到视图函数中,打印出了-----views-----
              f. 然后由于视图函数抛出异常,运行到test2类中处理exception的实例方法,打印出了-----test2-----exception----- 
              g. 接着运行到test1中处理exception的实例方法,打印出了-----test1-----exception----- 
              h. 然后从视图函数中拿到response,开始往回走,由于来的时候先经过test1,在经过test2,所以在回去的时候,要先经过test2,再经过test1,因此打印出了-----test2-----response-----
              i. 走过test2的response之后,走到test1的response,打印出了-----test1-----response-----
              j. 顺利走过自定义的中间件所有流程之后,拿着response通过wsgi返回给浏览器
     
    第五步:总结
              a. 如果视图函数中无异常,process_exception方法不执行。
              b. 这里医疗兵也很容易理解对吧?(:D)
     
     
    介绍了以上四员大将之后,最后一个要出场的家伙露面的机会不多(使用较少),但还是要介绍对吧!
    process_template_response的个人简介
    process_template_response(self, request, response)process_view(self, request, view_func, view_args, view_kwargs)
    request是HttpRequest对象。
    response是TemplateResponse对象(由视图函数或者中间件产生)。
    view_args是将传递给视图的位置参数的列表。
    view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
    process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。
    第一步:写入中间件到mymiddleware中
    # mymiddleware.py
     
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import render, redirect, HttpResponse
     
    class test1(MiddlewareMixin):
        def process_request(self, request):
            print('-----test1-----request-----')
            return
        def process_response(self, request, response):
            print('-----test1-----response-----')
            return response
     
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-----test1-----view-----")
            print(view_func, view_func.__name__)
     
        def process_exception(self, request, exception):
            print(exception)
            print("-----test1-----exception-----")
            return HttpResponse(str(exception))
     
        def process_template_response(self, request, response):    # process_template_response实例化对象,有两个参数
            print("-----test1-----template-----")    #打印,用来分析顺序
            return response    #把response原路返回
     
    class test2(MiddlewareMixin):
        def process_request(self, request):
            print('-----test2-----request-----')
            return
        def process_response(self, request, response):
            print('-----test2-----response-----')
            return response
     
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-----test2-----view-----")
            print(view_func, view_func.__name__)
     
        def process_exception(self, request, exception):
            print(exception)
            print("-----test2-----exception-----")
     
        def process_template_response(self, request, response):
            print("-----test2-----template-----")    #用来与test1中的process_template_response做对比
            return response
     
    第二步:由于process_template_response的触发需要一定的条件(视图函数返回的对象有一个render()方法)
    # views.py
     
    from django.shortcuts import render, redirect, HttpResponse
     
    # Create your views here.
    def home(request):
        print('-----views-----')
     
        def render():
            print("-----render-----")
            return HttpResponse("home")
     
        rep = HttpResponse("OK")
        rep.render = render    # 这步是通过看源码发现可以在外部给它添加实例化方法
        return rep
     
    第三步:运行server,查看结果并分析
    -----test1-----request-----
    -----test2-----request-----
    -----test1-----view-----
    <function home at 0x02CCB0C0> home
    -----test2-----view-----
    <function home at 0x02CCB0C0> home
    -----views-----    # 打印函数中的views
    -----test2-----template-----    # 打印了test2中的process_template_response
    -----test1-----template-----    # 打印了test1中的process_template_response
    -----render-----    # 有意思的地方在这里,中间件执行完之后,又回到视图函数中执行render中的内容
    -----test2-----response-----
    -----test1-----response-----
     
    第四步:分析
              a. 首先运行test1类中处理request的实例方法,打印出了-----test1-----request-----
              b. 然后运行test2类中处理request的实例方法,打印出了-----test2-----request-----
              c. 接着运行test1类中处理view的实例方法,打印出了-----test1-----view-----       
              d. 然后运行test2类中处理view的实例方法,打印出了-----test2-----view-----
              e. 接着运行到视图函数中,打印出了-----views-----
              f.  然后跳过render函数,运行test2类中的处理template_response的实例方法,打印出了-----test2-----template-----
              g. 接着运行test1类中的处理template_response的实例方法,打印出了-----test1-----template-----
              h. 然后回过头去运行render函数,打印出了-----render-----
              i. 然后从视图函数中拿到response,开始往回走,由于来的时候先经过test1,在经过test2,所以在回去的时候,要先经过test2,再经过test1,因此打印出了-----test2-----response-----
              j. 走过test2的response之后,走到test1的response,打印出了-----test1-----response-----
              k. 顺利走过自定义的中间件所有流程之后,拿着response通过wsgi返回给浏览器
     
    第五步:总结
              a. 视图函数执行完之后,立即执行了中间件的process_template_response方法,顺序是倒序,先执行MD1的,在执行MD2的,接着执行了视图函数返回的HttpResponse对象的render方法,返回了一个新的HttpResponse对象,接着执行中间件的process_response方法。
     
     
     
    三、中间件的执行顺序

    1.request和response两兄弟的执行顺序
    请求到达中间件之后,先按照正序执行每个注册中间件的process_reques方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法,将HttpResponse对象返回给浏览器。
    也就是说:如MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法。
     
    2.两员守门大将(request、response)和宫廷守卫(view)的执行顺序
    process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。
    假如中间件3 的process_view方法返回了HttpResponse对象则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行。
     
    3.那么医疗兵(exception)和隐者(template)与它们之间又有什么关系呢?
    它们两个需要一定的触发条件:exception需要视图函数抛出一个异常;template需要视图函数返回的对象中有一个render()方法。
    a. 从下图可以看出,request如果返回none,则按照正常顺序从上到下(注册的顺序)依次执行;
    b. 如果在某个中间件中的request中出现了HttpResponse,那么就从这个中间件的处理response的方法倒序往回执行;
    c. request和view都是返回None才正常执行,而response是返回response才正常执行;
    d. 如果某个中间件的处理视图的实例化方法返回了HttpResponse,那么就不再执行后续中间件的处理view的方法,而是直接走到处理response,按照倒序往回执行
     
    a. 下面箭头向下的有request和view,说明只有它们两个是从上往下正常执行的;
    b. template和response用了虚线,表示两者的触发需要一定的条件;
     
    4.对比一下皇家守卫团的成员们!
     
    执行顺序
    何时执行
    返回值
    process_request
    按照注册顺序
    请求从wsgi拿到之后
    返回None,继续执行后续的中间件的process_request方法
    返回response , 不执行后续的中间件的process_request方法
    process_response
    按照注册顺序的倒序
    请求有响应的时候
    必须返回一个response对象
    process_view
    按照注册顺序
    在urls.py中找到对应关系之后 
    在执行真正的视图函数之前
    返回None,继续执行后续的中间件的process_view方法
    返回response,不执行后续中间件的process_view方法,
    从最后一个process_response返回
    process_exception
    按照注册顺序的倒序
    视图函数中抛出异常的时候才执行
    返回None,继续执行后续中间件的process_exception
    返回response,
    process_template_response
    按照注册顺序的倒序
    视图函数执行完,
    在执行视图函数返回的响应对象的render方法之前
    返回None,继续执行后续中间件的process_exception
    返回response,
        
     
    5.接下来让我们来完整的看一下Django的流程吧!
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    【HDOJ】4982 Goffi and Squary Partition
    【HDOJ】4983 Goffi and GCD
    【算法导论】学习笔记——第7章 快速排序
    【算法导论】学习笔记——第6章 堆排序
    【HDOJ】4956 Poor Hanamichi
    【HDOJ】2492 Ping pong
    【Linux】鸟哥的Linux私房菜基础学习篇整理(十二)
    【Linux】鸟哥的Linux私房菜基础学习篇整理(十一)
    统计硬币
    放大的X
  • 原文地址:https://www.cnblogs.com/changwoo/p/9568510.html
Copyright © 2020-2023  润新知