Flask 请求源码分析

Posted A-a

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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))

 











以上是关于Flask 请求源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Flask框架cbv的写法请求与响应请求扩展session源码分析闪现

flask-day2——cbv源码分析模版语法请求与响应session及源码分析闪现请求扩展

Flask 请求源码分析

Flask系列之源码分析

Flask上下文源码分析

Flask请求和应用上下文源码分析