zoukankan      html  css  js  c++  java
  • django-中间件

    一、django生命周期

    写中间件之前呢?还是来说说django的生命周期,我再画下图贴上来,比较清楚
    
    1、浏览器向127.0.0.1:8000/index这个url发出一个请求
    2、首先网络通信都是基于socket的,那么django一样,通过wsgiref协议来的
    3、在socket对来的请求进行一系列处理,将请求带来的数据处理到request对吧
    4、然后再走到中间件
    5、通过路由层,分发到对应的视图函数,
    6、在视图函数中可能会去数据库取数据,到模板层渲染模板好了之后,
    7、再通过中间件,再到wsgiref进行处理,返回到浏览器前端
    
    对于模板层,视图层,路由层就十分熟悉不过了,就是多了一个中间件的那一层,
    从上面也应该能够知道,前端浏览器来的请求还是我后台django的响应,是不是都要走到中间件那层,
    那我们是不是在请求来的时候,先在中间件那里对请求做一些处理,再响应的时候,又做一次处理呢?

    二、中间件

    下面就说说怎么自定义一个中间件,那应该怎么去定义呢?
    不知道的话,那就是去看看django它的中间件是怎么样的,我们跟着写不就好了?
    在settings文件有个MIDDLEWARE的变量,它就是装着中间件的列表
    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',
        ]
    里面都是字符串,我应该怎么操作才能看到信息(这里可能有点啰嗦,但我还是要写,你知道可能别人不知道吧)
    复制其中的一个元素,将它的格式改为下面这种格式
    from django.middleware.clickjacking import XFrameOptionsMiddleware
    # 没错这就是导包的方法,那请问django它内部是怎么处理这个的呢?
    # 我猜测,只是猜测,无非就是在最前面加form,最后一个小数点改为import,就和上面不就一样了么。
    # 然后你再Ctrl+左击鼠标就能进去看源码了
    # 额额,再啰嗦一句,怎么看一个对象的源码,首先你要打印type(对象),在终端复制它的class到文件,
    # 然后操作和上面就是一样了
    
    你多复制几个django自带的中间件,你就会发现,这其实就是一个类,并且继承了MiddlewareMixin这个类,
    该类下面的隐藏方法不用去管,就看其他方法有没有相同之处,(这里你最好是自己去操作一下,这样才有印象)
    
    无非就是下面这几个方法:
    
    def process_request(self,request)
        pass
    
    def process_response(self,request,response)
        pass
    
    def process_view(self, request, callback, callback_args, callback_kwargs)
        pass
    
    看名字就应该能知道是干什么的吧,process_request:不就是处理请求嘛,process_response:处理响应。
    
    # 之前讲到浏览器来的请求,django的响应都会走到中间件,那么上面这么方法是不是也会执行到呢?
    # 如果可以的话,我就可以在中间件控制请求和响应了

    三、process_request方法

    自定义一个中间件:
        1、创建一个py文件,在里面写一个类,继承MiddlewareMixin
        2、再到settings里的MIDDLEWARE,添加刚刚创建类
    
        # 注意,1、你别光创建类,然后那么方法就不写了。
               2、根据需求写对应的方法,不一定要全部都写。
    
    下面是我写的中间件
    zhuyu.py:
    from django.utils.deprecation import MiddlewareMixin
    
    
    class Test1(MiddlewareMixin):
        def process_request(self, request):
            print('test1 request zhuyu')
    
    
    class Test2(MiddlewareMixin):
        def process_request(self, request):
            print('test2 request zhuyu')
    
    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',
        'zhuyu.Test1',
        'zhuyu.Test2',
        ]
    
    好了,我现在启动项目,并且发一个请求
    如果我没有猜错的话,应该会打印我定义的那两句话
    结果毋庸置疑:
        test1 request zhuyu
        test2 request zhuyu
    
    我们先看,先打印的是test1,然后才是test2,再去看settings中看看中间件配置
    zhuyu.Test1在zhuyu.Test2上方
    
    推断:我们是不是可以判断中间的执行循序是从上到下的呢?

    四、process_response方法

    process_request方法讲了,那在自定义中间中,再添加process_response
    
    def process_response(self,request,response):
        print ('test1 response')
    
    def process_response(self,request,response):
        print ('test2 response')
    
    
    这时我们django项目会报错,
    错误信息为:'NoneType' object has no attribute 'get'
    先不看这个错误,看看我终端的打印情况
    
    打印情况为: test1 request zhuyu
               test2 request zhuyu
               test2 response
               test1 response
    分析:前两行的我们知道原因,那为什么第三,四行却是test2,test1呢?
         难道不是从上往下的顺序?
    解答:首先计算机是不会出问题,出现这种情况,我们只能从结果反推测它怎么运行,
         什么时候会走到第三,四行,django处理完数据,响应回前端浏览器,走到中间件,
         先走到zhuyu.Test2,再是zhuyu.Test1,那我们就应该知道些什么了
    
    总结:1、浏览器过来的请求,执行顺序是从上往下,依次执行中间件里的process_request方法
         2、后台的响应,执行顺序为从下往上,依次执行process_response方法
    
    
    再说说那个错误:'NoneType' object has no attribute 'get'
    
    分析:出现这个错误是我们在给原先的中间件,添加process_response方法,那肯定是方法那里写的有问题
         我们看看那个方法:
        def process_response(self,request,response):
            print ('test2 response')
    
        执行该方法的时候,是我们后台已经处理完毕,然后返回到前台浏览器的过程
        那这几个参数代表些什么?
        request:代表request对象,也是前台来的请求对象
        response:这个呢?就是我们视图函数返回的HttpResponse对象,你说是不是,
        这个对象不应该是从中间件走到wsgiref那里进行处理,再到前台吗?
        再看我们的方法,它的返回值是None,所以才会报出上面那个错误
    
    解决方法:让这个方法的返回值是response就好了,return response
    思考:这个response就是HttpResponse对象,那我们是不是可以在该方法对这个response进行处理,
         再 return response
    
    跟着上面的总结继续写:
        3、如果执行process_response方法,必须返回response这个参数
    
    你是不是有这样的一个疑问?
        为什么我process_request方法,它的返回值是None,为什么不会出错呢?
    
    好吧!这其实是我疑问,为了解决这个疑问,我去看了它的父类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
    
    看到这段代码我大概知道中间件是怎么去执行的了,(这里我不太确定yy的对不对)
    1、首先settings中MIDDLEWARE是一个列表(也就是可迭代的),那应该就是对MIDDLEWARE进行for循环
    2、每循环出一个元素,这里是一个字符串,怎么知道到对应的类呢?我猜应该是和上面上个方法一样,对字符串进行修改
    3、找到.........................................................
    4、这里我卡住了,我知道大概顺序,但是不知道其内部到底怎么运行,难受,其实就是反射,__call__方法,哎,难弄!!!
    5、抽空还是用弄清楚。。卡在这里不舒服,知道的话可以邮件我1923790430@qq.com
    6、这里1,2的推断失效!

    五、process_view方法

    process_view(self, request, callback, callback_args, callback_kwargs)
    
    # 这里的request就是和视图函数的request一样
    # callback,该request中的url对应的视图函数
    # callback_args, callback_kwargs 该试图函数的有名参数,无名参数
    # 你也可以在这里调用,其实没多大的意义
    # 继续接着创建的中间件添加该方法,看看执行顺序
    process_view(self, request, callback, callback_args, callback_kwargs):
        print ('test1  view')
    process_view(self, request, callback, callback_args, callback_kwargs):
        print ('test2  view')
    
    执行看看效果:
            test1 request zhuyu
            test2 request zhuyu
            test1  view
            test2  view
            test2 response
            test1 response
    继续测试,我访问127.0.0.1:8000/index,并且在视图函数打印index,再看执行顺序
            test1 request zhuyu
            test2 request zhuyu
            test1  view
            test2  view
            index
            test2 response
            test1 response
    基于上面的测试,总结下执行顺序:
    process_request----->process_view----->执行视图函数----->process_response

    六、再看process_request方法

        它的返回值只能是是HttpResponse对象和None,None代表继续走下一个中间件的process_request方法,
        如果是HttpResponse,则会执行当前的process_response方法,相当于跳过了process_view和视图函数那块,
        为什么说只能是HttpResponse对象和None,因为有一层判断,判断process_request的返回值,为真的话,拿着
        该返回值HttpResponse执行process_response方法,因为process_response方法的参数必须为HttpResponse
        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
        这就是上面的依据,get_response这个方法我猜测一定是一直执行各个中间件的process_request,里面肯定是对象的
        调用,所以才会继续执行这个__call__方法,执行完所有process_request之后,再从最后一个中间件慢慢往上执行
        process_response。

     

    七、process_exception方法

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

    八、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,'测试')

    ## 好了中间件就更到这里

  • 相关阅读:
    TapTap推广统计逻辑
    广告推广测试
    背压(Backpressure)机制
    工作相关资料
    ElasticSearch问题记录
    bfrd collector性能排查
    Ubuntu13.10下安装HADOOP
    Hadoop各商业发行版之比较
    Behave用户自定义数据类型
    Behave step matcher
  • 原文地址:https://www.cnblogs.com/zhuchunyu/p/10016419.html
Copyright © 2011-2022 走看看