• Flask框架整个流程源码解读


    Flask框架整个流程源码解读

    一.总的流程

    运行Flask其本质是运行Flask对象中的__call__,而__call__本质调用wsgi_app的方法

    wsgi_app方法

        def wsgi_app(self, environ, start_response):
            #1.ctx是ResquestContext的对象,里面request
            ctx = self.request_context(environ)
            error = None
            try:
                try:
                     #2.就是ctx放到了Local对象
                    ctx.push()
                    #3.所有请求的执行函数的,包括请求扩展,真正的视图函数
                    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
                 #3. 请求之后的函数
                return response(environ, start_response)
            finally:
                if self.should_ignore_error(error):
                    error = None
                #4.最后把对应的request在request的队列中删除
                ctx.auto_pop(error)
    

    二.具体流程

    1.ctx = self.request_context(environ) environ

    请求相关的,ctx现在是包含request,session的RequestContext的对象

    源码

     1.1RequestContext(self, environ) self ,是app对象 environ,请求相关的
            1.2 RequestContext在实例化的时候的源码:
            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._implicit_app_ctx_stack = []
                self.preserved = False
                self._after_request_functions = []
    #这个RequestContext对象封装了,request 和seesoin
    

    2.ctx.push()

    这个ctx是RequestContext,那就执行RequestContext.push方法

    2.1RequestContext.push()的源码

     def push(self):
            #_request_ctx_stack是localStack的对象
            #self是ctx,把self也就ctx放入到local对象里面
            top = _request_ctx_stack.top
            if top is not None and top.preserved:
                top.pop(top._preserved_exc)
            app_ctx = _app_ctx_stack.top
            if app_ctx is None or app_ctx.app != self.app:
                app_ctx = self.app.app_context()
                app_ctx.push()
                self._implicit_app_ctx_stack.append(app_ctx)
            else:
                self._implicit_app_ctx_stack.append(None)
            if hasattr(sys, "exc_clear"):
                sys.exc_clear()
            _request_ctx_stack.push(self)  #这个里面的push源码如下
            if self.session is None:
                session_interface = self.app.session_interface
                self.session = session_interface.open_session(self.app, self.request)
                if self.session is None:
                    self.session = session_interface.make_null_session(self.app)
            if self.url_adapter is not None:
                self.match_request()
    

    源码中的push的源码

         #obj是ctx
        def push(self, obj):
            #obj是ctx,requestContext的对象
            rv = getattr(self._local, "stack", None)
            if rv is None:
                self._local.stack = rv = []
            rv.append(obj)
            return rv
    '''
            2.1.1 _request_ctx_stack.push(self)现在的self是ctx
            2.1.2 _request_ctx_stack是LocalStack()的对象
            2.1.3 LocalStack()的push把ctx传过来
            2.1.4 LocalStack()的push方法
            最终也就是ctx.push()他的最终目的:把当前的ctx放入到Local()里面     
    '''    
    

    3 response = self.full_dispatch_request()

    源码

         #这是服务器第一次请求时候执行的函数
                self.try_trigger_before_first_request_functions()
                try:
                    request_started.send(self)
                    #执行请求之前所有的函数,并且拿到请求之前的返回值
                    rv = self.preprocess_request()
                    if rv is None:
                        #这个是真正视图函数,如果我的请求之前函数没有返回值才会执行
                        rv = self.dispatch_request()
                except Exception as e:
                    rv = self.handle_user_exception(e)
                return self.finalize_request(rv)
        3.1 return self.finalize_request(rv)的源码:
            def finalize_request(self, rv, from_error_handler=False):
                response = self.make_response(rv)
                try:
                    #请求之后的函数,after_request
                    response = self.process_response(response)
                    request_finished.send(self, response=response)
                except Exception:
                    if not from_error_handler:
                        raise
                    self.logger.exception(
                        "Request finalizing failed with an error while handling an error"
                    )
                return response
    
    • 告诉我们了几个函数的运行顺序

    • 解释了为什么frist_request为什么只第一次启动项目页面才会运行,因为运行后值就不为空后面事物也不走first相关函数了

    • 也解释了为什么before_request有返回值后就不会运行真正的视图,因为有返回值后rv就不为空了就不运行下面的函数

    • 同样我们如果顶进去process_response的相关函数可以看出来他必须要传入response的对象以及返回response的对象

    4.我们第三步的任意一个地方都能使用我们的request,session是怎么获取的?

      4.1 我们在flask导入request,这个request是一个全局的变量,我们怎么通过request区分我当前的request对象(environ)
         我们发现request是LocalProxy的对象
        4.2 当我们用全局的request.属性的时候,就会去找LocalProxy的对象,但是我们发现里面根本就没有
            那他一定执行LocalProxy对象的__getattr__方法
        4.3 我们现在来看LocalProxy对象的__getattr__方法的源码:
            #name我们要获取属性名
            def __getattr__(self, name):
                if name == "__members__":
                    return dir(self._get_current_object())
                #form
                #self._get_current_object()就是ctx里面的request,
                return getattr(self._get_current_object(), name)
            4.3.1 通过反射self._get_current_object()对象,来找我们属性,也就是name
               self._get_current_object()的源码:
                    def _get_current_object(self):
                        if not hasattr(self.__local, "__release_local__"):
                            return self.__local()
                        try:
                            #self.__local就实例化传过来的偏函数,
                            return getattr(self.__local, self.__name__)
                        except AttributeError:
                            raise RuntimeError("no object bound to %s" % self.__name__)
    
               4.3.1.1 return getattr(self.__local, self.__name__)那这里self.__local是谁?
                        def __init__(self, local, name=None):
                              object.__setattr__(self, "_LocalProxy__local", local)
                        self.___local为local
                        这个local为实例化的时候传的
                    4.3.1.1.1 这个实例化的时候的操作
                       request = LocalProxy(partial(_lookup_req_object, "request"))
                       4.3.1.1的local就是 partial(_lookup_req_object, "request")的地址
                    4.3.1.1.2 _lookup_req_object的源码:
                        #调用的时候 partial(_lookup_req_object, "request")
                        #现在的name就是"request"
                        def _lookup_req_object(name):
                            # top是当前线程的ctx
                            top = _request_ctx_stack.top
                            if top is None:
                                raise RuntimeError(_request_ctx_err_msg)
                            #找top里面的request
                            # ctx找request
                            return getattr(top, name)
                    4.3.1.1.2 我们来看这个_request_ctx_stack.top的top方法
                            def top(self):
                                try:
                                    return self._local.stack[-1]
                                except (AttributeError, IndexError):
                                    return None
                            我们发现这个self._local是Local()对象,这样就把ctx拿到了
    
  • 相关阅读:
    Python的包管理工具Pip
    C语言移位运算符
    malloc函数具体解释
    HDU
    Java中Scanner的使用方法
    DOS call 中的%cd%,当前文件夹演示
    没有找到MSVCR100.dll解决方法
    什么是响应式表格(响应式表格和普通表格的区别)
    Redis和Memcache和MongoDB简介及区别分析(整理)
    GIT将本地项目上传到Github(两种简单、方便的方法)
  • 原文地址:https://www.cnblogs.com/pythonywy/p/11604572.html
Copyright © 2020-2023  润新知