• Flask 请求源码分析


    执行app.run()方法:

     def run(self, host=None, port=None, debug=None, **options):
            from werkzeug.serving import run_simple
            if host is None:
                host = '127.0.0.1'
            if port is None:
                server_name = self.config['SERVER_NAME']
                if server_name and ':' in server_name:
                    port = int(server_name.rsplit(':', 1)[1])
                else:
                    port = 5000
            if debug is not None:
                self.debug = bool(debug)
            options.setdefault('use_reloader', self.debug)
            options.setdefault('use_debugger', self.debug)
            try:
                #执行,self=app---->执行Flask类的__call__方法
                run_simple(host, port, self, **options)
            finally:
                self._got_first_request = False
    run方法

    执行Flask.__call__方法:

     #包含请求相关的所有信息
        def __call__(self, environ, start_response):
            """Shortcut for :attr:`wsgi_app`."""
            return self.wsgi_app(environ, start_response)

    执行wsgi_app()方法:

     def wsgi_app(self, environ, start_response):
            #获取请求数据,并进行封装和加工,
            ctx = self.request_context(environ)
            #将RequestContext(request,session)封装在Local中
            '''
            {'唯一标识':{'stack':[RequestContext(request,session),]}
            }
            '''
            ctx.push()
            error = None
            try:
                try:
                    #调用视图函数
                    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)

    执行RequestContext.__init__()和push()方法:

    __init__():获取request

     def __init__(self, app, environ, request=None):
            self.app = app
            if request is None:
                #实际是一个Request对象,将request信息封装到Request(environ)中,并赋值给RequestContext对象中
                request = app.request_class(environ)
            self.request = request
            self.url_adapter = app.create_url_adapter(self.request)
            self.flashes = None
            self.session = None

    push()方法:将RequestContext对象添加到Local中,获取/创建session

       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 = 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()
            #将当前requestContext对象放到Local中,
            _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 (e.g. code that access database information
            # stored on `g` instead of the appcontext).
    
            #赋值操作将self.session=SecureCookieSession()
    #
    SecureCookieSession()实际上是一个字典
            self.session = self.app.open_session(self.request)
            if self.session is None:
    #用户第一次进来session={}执行,返回NullSession self.session
    = self.app.make_null_session()
    当请求进来时:执行open_session()方法:
    class SecureCookieSessionInterface(SessionInterface):
        def open_session(self, app, request):
            s = self.get_signing_serializer(app)
            if s is None:
                return None
            #去cookie中获取session作为key,所对应的值(包含了当前用户所有的session数据)
            val = request.cookies.get(app.session_cookie_name)
            #没有
            if not val:
                #返回SecureCookieSession
                return self.session_class()
            max_age = total_seconds(app.permanent_session_lifetime)
            try:
                #val存在的话
                #解密 将加密的字符串解密程字典
                data = s.loads(val, max_age=max_age)
                return self.session_class(data)
            except BadSignature:
                return self.session_class()

    这是我们已经将RequestContext(request,session)的对象放到Local中,并且request和session的初始值也获取到了,

    上面方法就是为了执行下面两句:

     ctx = self.request_context(environ)
    ctx.push()


    执行上面的语句后我们在wagi_app()方法中继续向下执行,
    触发视图函数
    Flask类中
        def full_dispatch_request(self):
            """Dispatches the request and on top of that performs request
            pre and postprocessing as well as HTTP exception catching and
            error handling.
    
            .. versionadded:: 0.7
            """
            #执行@before_first_request所装饰的所有函数
            self.try_trigger_before_first_request_functions()
            try:
                request_started.send(self)
                #执行@before_request装饰的所有函数 看是否有返回值
                rv = self.preprocess_request()
                if rv is None:
                    rv = self.dispatch_request()
            except Exception as e:
                rv = self.handle_user_exception(e)
            #执行@after_request所装饰的所有函数
            return self.finalize_request(rv)

    执行finalize_request()方法:

        def finalize_request(self, rv, from_error_handler=False):
            """Given the return value from a view function this finalizes
            the request by converting it into a response and invoking the
            postprocessing functions.  This is invoked for both normal
            request dispatching as well as error handlers.
    
            Because this means that it might be called as a result of a
            failure a special safe mode is available which can be enabled
            with the `from_error_handler` flag.  If enabled, failures in
            response processing will be logged and otherwise ignored.
    
            :internal:
            """
            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

    执行process_response()方法

        def process_response(self, response):
            """Can be overridden in order to modify the response object
            before it's sent to the WSGI server.  By default this will
            call all the :meth:`after_request` decorated functions.
    
            .. versionchanged:: 0.5
               As of Flask 0.5 the functions registered for after request
               execution are called in reverse order of registration.
    
            :param response: a :attr:`response_class` object.
            :return: a new response object or the same, has to be an
                     instance of :attr:`response_class`.
            """
            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)
            #保存session
            if not self.session_interface.is_null_session(ctx.session):
                self.save_session(ctx.session, response)
            return response

    具体是怎么保存session的呢?

    看save_session()方法

    session.py中

        def save_session(self, app, session, response):
            domain = self.get_cookie_domain(app)
            path = self.get_cookie_path(app)
    
            # Delete case.  If there is no session we bail early.
            # If the session was modified to be empty we remove the
            # whole cookie.
            if not session:
                if session.modified:
                    response.delete_cookie(app.session_cookie_name,
                                           domain=domain, path=path)
                return
    
            # Modification case.  There are upsides and downsides to
            # emitting a set-cookie header each request.  The behavior
            # is controlled by the :meth:`should_set_cookie` method
            # which performs a quick check to figure out if the cookie
            # should be set or not.  This is controlled by the
            # SESSION_REFRESH_EACH_REQUEST config flag as well as
            # the permanent flag on the session itself.
            if not self.should_set_cookie(app, session):
                return
    
            httponly = self.get_cookie_httponly(app)
            secure = self.get_cookie_secure(app)
            expires = self.get_expiration_time(app, session)
    #加密操作 val
    = self.get_signing_serializer(app).dumps(dict(session))
    #将session保存在cookie中 response.set_cookie(app.session_cookie_name, val, expires
    =expires, httponly=httponly, domain=domain, path=path, secure=secure)

    globals.py

    def _lookup_req_object(name):
        # top实际是一个RequestContext对象
        '''
                {
                '唯一标识':{'stack':[RequestContext(request,session),]}
                }
                '''
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
    
        #得到session:RequestContext(request,session).session
        #得到request:RequestContext(request,session).request
        return getattr(top, name)
    
    
    def _lookup_app_object(name):
        top = _app_ctx_stack.top
        if top is None:
            raise RuntimeError(_app_ctx_err_msg)
        return getattr(top, name)
    
    
    def _find_app():
        top = _app_ctx_stack.top
        if top is None:
            raise RuntimeError(_app_ctx_err_msg)
        return top.app
    
    
    # context locals
    _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'))
  • 相关阅读:
    如何解决加载动态链接库DLL失败,返回0,GetLastError返回错误码126
    如何实现点击激活窗体同时窗体不跑到最前覆盖其他窗体
    数据库04
    数据库03
    数据库02
    数据库01
    Linux02
    Linux01
    软件质量
    HTML04
  • 原文地址:https://www.cnblogs.com/ctztake/p/8245057.html
Copyright © 2020-2023  润新知