• flask请求上下文 及相关源码


    flask上下文对象:
        1.RequestContext请求上下文:
            Request:request请求对象,封装了http请求的内容
            Session:根据请求中的cookie,重新载入该访问者相关的会话信息
        
        2.AppContext程序上下文:
            g对象:处理请求时用作临时存储的对象
            current_app:当前激活程序的程序实例
            
        3.生命周期:
            1.current_app的生命周期最长,只要当前程序实例还在运行,都不会失效
            2.Request和g的生命周期为一次请求期间,当请求处理完成后,生命周期也就完结了
            3.Session就是传统意义上的session,只要还未失效,那么不同的请求可以访问到同一个session
            
    源码:
        1. 全局的变量
            _request_ctx_stack = LocalStack()
            _app_ctx_stack = LocalStack()
            current_app = LocalProxy(_find_app)
            request = LocalProxy(partial(_lookup_req_object, "request"))
            session = LocalProxy(partial(_lookup_req_object, "session"))
            g = LocalProxy(partial(_lookup_app_object, "g")) 
    
        2. flask请求上下文全部与源码:
            def wsgi_app(self, environ, start_response): 
                #environ请求相关的所有数据,ctx就是ResquestContext的对象,包含request
                ctx = self.request_context(environ)
                error = None
                try:
                    try:
                        #把request放进去了,Local()
                        ctx.push()
                        #执行视图函数,请求扩展
                        response = self.full_dispatch_request()
                    except Exception as e:
                        error = e
                        response = self.handle_exception(e)
                    except:  # noqa: B001
                        error = sys.exc_info()[1]
                        raise
                    #返回响应
                    return response(environ, start_response)
                finally:
                    if self.should_ignore_error(error):
                        error = None
                    ##把request,和session删除
                    ctx.auto_pop(error)
                    
            2.1 ctx = self.request_context(environ),这个environ是请求相关的
                self.request_context(environ)这个话的本质是执行return RequestContext(self, environ)
                现在传给RequestContext的变量self是当前app,environ是请求相关的
                2.1.1我们发现RequestContext是一个类,类加括号是就实例化得到对象,
                     这个对象里面有request,session等:
                    app-->就当前的flask对象,environ请求相关的
                     def __init__(self, app, environ, request=None, session=None):
                            self.app = app
                            if request is None:
                                request = app.request_class(environ)
                            self.request = request
                            self.url_adapter = None
                            try:
                                self.url_adapter = app.create_url_adapter(self.request)
                            except HTTPException as e:
                                self.request.routing_exception = e
                            self.flashes = None
                            self.session = session
                            self.preserved = False
                            self._preserved_exc = None           
                            self._after_request_functions = []
                2.1.2 这样我们执行ctx = self.request_context(environ),
                      得到是RequestContext的对象这个对象里面有request,session等,      
            
            2.2  ctx.push()的源码:
                执行的是RequestContext里面的push方法: _request_ctx_stack.push(self),当前的self是ctx
                我们发现_request_ctx_stack就是LocalStack() ,
                 _request_ctx_stack.push(self)就是执行LocalStack的push,并且把ctx传过来了
                2.2.1   _request_ctx_stack.push(self)的源码:
                    #obj就是ctx,self._local是Local对象,用来区分不同线程,协程的数据
                    def push(self, obj):
                        rv = getattr(self._local, "stack", None)
                        if rv is None:                    
                            # self._local=>stack-->storage['线程id']['stack']=[]
                            self._local.stack = rv = []
                         # self._local=>stack-->storage['线程id']['stack']=[ctx,]
                        rv.append(obj)
                        return rv
        
        3. request是怎样取到我放到 Local对象里面的ctx在里面的request
        request = LocalProxy(partial(_lookup_req_object, "request"))
        当我们request.form时候就会找LocalProxy(partial(_lookup_req_object, "request"))要form属性
            3.1当要request属性时候,就会执行LocalProxy的getattr,源码:
            #name=我们要的属性,比如request.form的form,name=form
             def __getattr__(self, name):
                if name == "__members__":
                    return dir(self._get_current_object())
                #name-->form,self._get_current_object()===>ctx.request,form
                return getattr(self._get_current_object(), name)
                
                最后我们发现self._get_current_object()是我们在实例化
                  LocalProxy(partial(_lookup_req_object, "request"))
                传过来的偏函数执行结果。
                
                3.1.1:self._get_current_object()要执行的代码是:
                      def _get_current_object(self):
                        if not hasattr(self.__local, "__release_local__"):
                            return self.__local()
                        try:
                            return getattr(self.__local, self.__name__)
                        except AttributeError:
                            raise RuntimeError("no object bound to %s" % self.__name__)
                    3.1.1.1 self._get_current_object()这个里面的self.__local():
                        我们发先这个self.__local是在实例化的时候传过来def __init__(self, local, name=None):  
                            object.__setattr__(self, "_LocalProxy__local", local)
                        我们发现传过来的是partial(_lookup_req_object, "request")偏函数:
                            偏函数源码:
                                #name=request
                                def _lookup_req_object(name):
                                    top = _request_ctx_stack.top
                                    #top就是我们前面放进去ctx
                                    if top is None:
                                        raise RuntimeError(_request_ctx_err_msg)
                                    #getattr(top, name)的意思就是到ctx里面找request
                                    return getattr(top, name)
                        
                        3.1.1.1.1  top = _request_ctx_stack.top: # _request_ctx_stack是 LocalStack()的对象:
                                    源码:
                                        @property
                                        def top(self):
                                            try:
                                                #返回ctx
                                                return self._local.stack[-1]
                                            except (AttributeError, IndexError):
                                                return None
                            
    
    
            
    源码
        总结:其实操作flask的请求上下文就是操作Local中的字典__storage__
            
            1.通过RequestContext类首先实例化ctx请求上下文对象,其内部包含请求对象
    
            2.入栈,通过请求上下文对象的类的push()方法触发了LocalStack类的push() 方法,从而添加到Local类中的字典里。
    
            3.观察导入的request源码 ,通过观察LocalProxy的源码,最后触发了LocalStack的top()方得到上下文对象,再得到请求对象,
                从而实现reuqest的功能。到请求对象,从而实现reuqest的功能。
    
            4.出栈通过请求上下文对象的类的方法,触发了LocalStack的的pop()方法从而从字典中删除掉当前线程或当前携程的请求信息。
  • 相关阅读:
    PSP ISO游戏运行必备工具:ISO TOOL 1.970 功能一览&图文教程
    Linux防火墙(原书第3版) 电子书籍
    iptables的相关概念和数据包的流程(图)
    oracle数据库远程连接服务器配置tnsnames
    编程感悟
    工作任务三 打印表单数据
    UltraWebTree的使用心得
    DropDownList应用
    使用UltraWebTree时,如何在刷新后展开之前选中的节点,并绑定相关数据
    webgrid 添加行是不允许相同
  • 原文地址:https://www.cnblogs.com/wyf20190411-/p/13774949.html
Copyright © 2020-2023  润新知