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()方法从而从字典中删除掉当前线程或当前携程的请求信息。

 

posted @ 2020-10-06 19:49  54菜鸟  阅读(182)  评论(0编辑  收藏  举报