07 Flask源码之:用户请求过来流程

1.创建ctx = RequestContext对象

  • RequestContext对象封装Request对象

  • RequestContext对象封装session数据

  • 源码实现:

    def wsgi_app(self, environ, start_response):
        """
            ctx = RequestContext(self, environ)
            """
        # 2.1 创建RequestContext对象
        ctx = self.request_context(environ)
    def request_context(self, environ):
        return RequestContext(self, environ)
    request_class = Request
    
    class RequestContext(object):
        def __init__(self, app, environ, request=None, session=None):
            self.app = app
            if request is None:
                """
                request_class = Request
                """
                request = app.request_class(environ)
            self.request = request
            self.session = session

2. 创建app_ctx = AppContext对象

  • AppContext对象封装App对象

  • AppContext对象封装g

  • 源码实现:

    def wsgi_app(self, environ, start_response):
        """
            ctx = RequestContext(self, environ)
            """
        # 2.1 创建RequestContext对象
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                """
                2.2 执行ctx.push
                    - app_ctx = 创建AppContext对象(app,g)
                    - 将app_ctx放入local中
                    - 将ctx放入到local中
                    - session赋值
                    - 路由匹配
                 """
                ctx.push()
        def push(self):
            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 = AppContext(app)
                """
                # 创建appcontext对象
                app_ctx = self.app.app_context()
    def app_context(self):
        return AppContext(self)
    class AppContext(object):
        def __init__(self, app):
            self.app = app
            self.g = app.app_ctx_globals_class()

3. 将ctx对象、app_ctx对象放到local中

  • 然后ctx.push触发将 ctx对象,通过自己的LocalStack对象将其放入到Local中

  • 然后app_ctx.push触发将 app_ctx对象,通过自己的LocalStack对象将其放入到Local中

  • Local的本质是以线程ID为key,以{“stack”:[]}为value的字典

    存储结构:{
    1111:{“stack”:[ctx,]}
    };

    ​ {
    ​ 1111:{“stack”:[app_ctx,]}
    ​ }

  • 源码示例:

        def push(self):
            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 = AppContext(app)
                """
                # 创建appcontext对象
                app_ctx = self.app.app_context()
                # push将app_ctx放入到local中
                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()
            """
            self = ctx = RequestContext(self, environ)
            """
            # push将ctx放入到local中
            _request_ctx_stack.push(self)
    
            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:
                # 路由匹配,将匹配到的endpoint放到request.url_rule中
                self.match_request()

4. 执行所有before_request函数以及所有的视图函数

  1. 执行full_dispatch_request函数
  2. 执行所有的before_request函数
  3. 执行视图函数
  • 源码示例:

    def wsgi_app(self, environ, start_response):
        """
            ctx = RequestContext(self, environ)
        """
        #2.1 创建RequestContext对象
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                # 做了很多事
                """
                    2.2 执行ctx.push
                        - app_ctx = 创建AppContext对象(app,g)
                        - 将app_ctx放入local中
                        - 将ctx放入到local中
                        - session赋值
                        - 路由匹配
                 """
                ctx.push()
                # 2.3 执行before_request/视图/after_request (处理session)
                response = self.full_dispatch_request()
    def full_dispatch_request(self):
        # 触发所有的before_first_request_funcs函数
        # 只在启动程序后,第一个请求到来时执行
        self.try_trigger_before_first_request_functions()
        try:
            # 信号,暂留
            request_started.send(self)
            # 执行before_request_funcs函数,如果有返回值就不执行视图函数了
            rv = self.preprocess_request()
            if rv is None:
                # 执行视图函数
                rv = self.dispatch_request()
                except Exception as e:
                    rv = self.handle_user_exception(e)
                    # 视图函数执行之后
                    #  1.执行所有的after_request
                    #  2.保存session
                    return self.finalize_request(rv)

5. 执行所有after_request函数

  • session会加密返回给用户浏览器放到cookie中

  • 源码示例:

    def finalize_request(self, rv, from_error_handler=False):
        # 将rv视图函数返回值,封装到Reponse对象中
        response = self.make_response(rv)
        response = self.process_response(response)
        return response
    response_class = Response
    
    def make_response(self, rv):
        if not isinstance(rv, self.response_class):
            if isinstance(rv, (text_type, bytes, bytearray)):
                rv = self.response_class(rv, status=status, headers=headers)
                return rv
    def process_response(self, response):
        ctx = _request_ctx_stack.top
        funcs = ctx._after_request_functions
        # 执行所有的after_request_funcs
        funcs = chain(funcs, reversed(self.after_request_funcs[None]))
        for handler in funcs:
            response = handler(response)
            if not self.session_interface.is_null_session(ctx.session):
                # 保存session
                self.session_interface.save_session(self, ctx.session, response)
                return response

6. 销毁ctx和app_ctx

  • 如果请求结束不销毁ctx和app_ctx的话,会造成内存泄漏

  • 分别调用ctx和app_ctx的pop方法

  • 源码示例:

    def wsgi_app(self, environ, start_response):
        """
            ctx = RequestContext(self, environ)
        """
        #2.1 创建RequestContext对象
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                """
                    2.2 执行ctx.push
                        - app_ctx = 创建AppContext对象(app,g)
                        - 将app_ctx放入local中
                        - 将ctx放入到local中
                        - session赋值
                        - 路由匹配
                 """
                ctx.push()
                # 2.3 执行before_request/视图/after_request (处理session)
                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
                            # 2.4 销毁ctx/app_ctx
                            ctx.auto_pop(error)
    
    def auto_pop(self, exc):
        self.pop(exc)
    def pop(self, exc=_sentinel):
        app_ctx = self._implicit_app_ctx_stack.pop()
        finally:
            rv = _request_ctx_stack.pop()
            if app_ctx is not None:
                app_ctx.pop(exc)
01-09 20:38