Flask框架(五) —— session源碼分析

Flask框架(五) —— session源碼分析

session源碼分析

一、請求來了,執行__call__方法

# 請求來了執行 __call__方法
if __name__ == '__main__':
    app.__call__
    app.run()

二、__call__方法

def __call__(self, environ, start_response):
    """The WSGI server calls the Flask application object as the WSGI application. This calls :meth:`wsgi_app` which can be wrapped to applying middleware."""
    return self.wsgi_app(environ, start_response)

三、調用__call__方法

def wsgi_app(self, environ, start_response):
        # 1.獲得request,和空的session
        # ctx是RequestContext的對象,對象裏面有空的session和request
        ctx = self.request_context(environ)     
        error = None
        try:
            try:
                # 2.從request中的cookie中取出value,解密過轉成session對象 --> open_session
                ctx.push()
                # 3.路由映射到函數,執行函數,而後保存session --> save_session,請求結束
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

3.一、ctx = self.request_context(environ) --- 獲得空的session

# 1. 得到RequestContext對象 ---> ctx
    def request_context(self, environ):  
        return RequestContext(self, environ)
    
    
    class RequestContext(object): 
        def __init__(self, app, environ, request=None):
            self.app = app
            if request is None:
                request = app.request_class(environ)     # request_class = Request
            self.request = request    
            self.url_adapter = app.create_url_adapter(self.request)
            self.flashes = None
            self.session = None

3.二、ctx.push() --- 調用open_session方法

# 2.ctx.push() --- 調用open_session方法
    def push(self):
        """Binds the request context to the current context."""
        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)

        # Before we push the request context we have to ensure that there
        # is an application context.
        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)

        # Open the session at the moment that the request context is available.
        # This allows a custom open_session method to use the request context.
        # Only open a new session if this is the first time the request was
        # pushed, otherwise stream_with_context loses the session.
        
        # 從request中的cookie中取出value,(有解密過程)轉成session對象
        # 正常狀況下此時session中有值了
        # 若是request中沒有cookie,那麼session中仍然沒有值
        if self.session is None:
            # session_interface = SecureCookieSessionInterface()
            session_interface = self.app.session_interface 
            # 2.1 opensession(),SecureCookieSessionInterface類中
            self.session = session_interface.open_session(
                self.app, self.request
            )

            if self.session is None:
                self.session = session_interface.make_null_session(self.app)

3.2.一、session_interface.open_session() --- 從經過session的名字取出cookie的值轉成session

# 2.1.session_interface.open_session()
class SecureCookieSessionInterface(SessionInterface):
    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        # 經過session的名字取出cookie的值,key:value形式,獲得的是json格式的字符串
        val = request.cookies.get(app.session_cookie_name)
        if not val:
            return self.session_class()
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
            # 將json格式字符串轉成字典
            data = s.loads(val, max_age=max_age)
            # 返回一個特殊的字典
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

3.三、self.full_dispatch_request() --- 路由分發,執行函數,寫入session

# 3.self.full_dispatch_request()
    1 執行before_request
    2 執行視圖函數
    3 把session寫入
        if not self.session_interface.is_null_session(ctx.session):
            self.session_interface.save_session(self, ctx.session, response)

    def full_dispatch_request(self):
        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)
        # 3.1 把session保存寫入cookie
        return self.finalize_request(rv)

3.3.一、self.finalize_request(rv) --- 調用save_session()保存寫入session

# 3.1.self.finalize_request(rv)
    def finalize_request(self, rv, from_error_handler=False):
        response = self.make_response(rv)
        try:
            # 利用save_session 保存寫入session
            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
3.3.1.一、self.process_response(response) --- 調用save_session方法
# 保存寫入session 
    def process_response(self, response):
        ctx = _request_ctx_stack.top
        bp = ctx.request.blueprint
        funcs = ctx._after_request_functions
        if bp is not None and bp in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
        if None in self.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):
            # 調用save_session方法保存寫入session
            self.session_interface.save_session(self, ctx.session, response)
        return response
save_session()方法
class SecureCookieSessionInterface(SessionInterface):
    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)

        # If the session is modified to be empty, remove the cookie.
        # If the session is empty, return without setting the cookie.
        if not session:
            if session.modified:
                response.delete_cookie(
                    app.session_cookie_name,
                    domain=domain,
                    path=path
                )

            return

        # Add a "Vary: Cookie" header if the session was accessed at all.
        if session.accessed:
            response.vary.add('Cookie')

        if not self.should_set_cookie(app, session):
            return

        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        samesite = self.get_cookie_samesite(app)
        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))
        response.set_cookie(
            app.session_cookie_name,
            val,
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite
        )
博客內容僅供參考,部分參考他人優秀博文,僅供學習使用
相關文章
相關標籤/搜索