Flask源码阅读(七)——上下文变量

本文深入解析Flask框架中的上下文概念,包括application context和request context。阐述了它们的区别和作用,以及如何通过_RequestContext将两者关联。通过源码分析,展示了Flask如何在每次HTTP请求时创建Request对象,并且讨论了session的处理过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.为了避免大良可有可无的参数把视图函数弄得一团糟,Flask使用上下文临时把某些对象变为全局可访问。

上下文(context)分为程序上下文(application)和请求(request context)上下文。

变量名上下文说 明
current_app程序上下文当前激活程序的程序实例
g程序上下文处理请求时用作临时存储的对象。每次请求都会重设这个变量
request请求上下文请求对象,封装了客户端发出的HTTP请求中的内容
session请求上下文用户会话,用于存储请求之间需要“记住”的值的词典

程序上下文被推送后,就可以在线程中使用current_app和g变量。同样,请求上下文被推送后,就可以使用request和session变量。
这里需要通俗地解释一下application context与request context:

  1. application 指的就是当你调用app = Flask(__name__)创建的这个对象app;
  2. request 指的是每次http请求发生时,WSGI server(比如gunicorn)调用Flask.__call__()之后,在Flask对象内部创建的Request对象;
  3. application 表示用于响应WSGI请求的应用本身,request 表示每次http请求;
  4. application的生命周期大于request,一个application存活期间,可能发生多次http请求,所以,也就会有多个request

2.源码

class Flask(object):

    def request_context(self, environ):
        """Creates a request context from the given environment and binds
        it to the current context.  This must be used in combination with
        the `with` statement because the request is only bound to the
        current context for the duration of the `with` block.

        Example usage::

            with app.request_context(environ):
                do_something_with(request)

        :params environ: a WSGI environment
        """
        return _RequestContext(self, environ)

在Flask类中,每次请求都会调用这个request_context函数。这个函数则会创建一个_RequestContext对象。

class _RequestContext(object):
    """The request context contains all request relevant information.  It is
    created at the beginning of the request and pushed to the
    `_request_ctx_stack` and removed at the end of it.  It will create the
    URL adapter and request object for the WSGI environment provided.
    """

    def __init__(self, app, environ):
        self.app = app
        self.url_adapter = app.url_map.bind_to_environ(environ)
        self.request = app.request_class(environ)
        self.session = app.open_session(self.request)
        self.g = _RequestGlobals()
        self.flashes = None

    def __enter__(self):
        _request_ctx_stack.push(self)

    def __exit__(self, exc_type, exc_value, tb):
        # do not pop the request stack if we are in debug mode and an
        # exception happened.  This will allow the debugger to still
        # access the request object in the interactive shell.
        if tb is None or not self.app.debug:
            _request_ctx_stack.pop()

值得注意的是:这个对象在创建时,将Flask实例的本身作为实参传入_RequestContext自身,因此,
self.app = Flask()。
所以,虽然每次http请求都会创建一个_RequestContext对象,但是,每次创建的时候都会将同一个Flask对象传入该对象的app成员变量,使得:
由同一个Flask对象响应的请求所创建的_RequestContext对象的app成员变量都共享同一个application
通过在Flask对象中创建_RequestContext对象,并将Flask自身作为参数传入_RequestContext对象的方式,实现了多个request context对应一个application context 的目的。

源码中的g:

self.g = _RequestGlobals()

回溯源码:

class _RequestGlobals(object):
    pass

源码中的session:

self.session = app.open_session(self.request)

回溯open_session:

    #: if a secret key is set, cryptographic components can use this to
    #: sign cookies and other things.  Set this to a complex random value
    #: when you want to use the secure cookie for instance.
    secret_key = None
 #: The secure cookie uses this for the name of the session cookie
    session_cookie_name = 'session'

def open_session(self, request):
        """Creates or opens a new session.  Default implementation stores all
        session data in a signed cookie.  This requires that the
        :attr:`secret_key` is set.

        :param request: an instance of :attr:`request_class`.
        """
        key = self.secret_key
        if key is not None:
            return SecureCookie.load_cookie(request, self.session_cookie_name,
                                            secret_key=key)

同时,源码中还留有save_session()这个函数:

 def save_session(self, session, response):
        """Saves the session if it needs updates.  For the default
        implementation, check :meth:`open_session`.

        :param session: the session to be saved (a:class:`~werkzeug.contrib.securecookie.SecureCookie`object)
        :param response: an instance of :attr:`response_class`
        """
        if session is not None:
            session.save_cookie(response, self.session_cookie_name)

可以看到,在session不为空的时候,将self.session_cookie_name保存到response中。
接下来,看self.request = app.request_class(environ)这句。
由于app成员变量就是app = Flask(__name__)这个对象,所以,app.request_class就是Flask.request_class

在Flask类的定义中:

request_class = Request # Request 是一个类,定义如下:

class Request(RequestBase):
    """The request object used by default in flask.  Remembers the
    matched endpoint and view arguments.

    It is what ends up as :class:`~flask.request`.  If you want to replace
    the request object used you can subclass this and set
    :attr:`~flask.Flask.request_class` to your subclass.
    """

    def __init__(self, environ):
        RequestBase.__init__(self, environ)
        self.endpoint = None
        self.view_args = None

所以:
self.request = app.request_class(environ)实际上是创建了一个Request对象。
由于,一个http请求对应一个_RequestContext对象的创建,而每个_RequestContext对象的创建对应一个Request对象的创建,所以,每个http请求对应一个Request对象。

到这里想必已经很清楚了:

application 就是指app = Flask(name)对象
request 就是对应每次http 请求创建的Request对象
flask通过_RequestContext将app与Request关联起来

总结

  1. app = Flask(name)创建了application,
    这个application对应的上下文,就是application context

  2. Flask每响应一个http请求,就会创建一个Request对象,这个request对象对应的上下文,就是request context


1月12日


    def match_request(self):
        """Matches the current request against the URL map and also
        stores the endpoint and view arguments on the request object
        is successful, otherwise the exception is stored.
        """
        rv = _request_ctx_stack.top.url_adapter.match()
        request.endpoint, request.view_args = rv
        return rv

对resquest匹配当前的URL映射,同时在request对象上储存端点和视图参数成功,否则异常就被储存。
1月13日

def dispatch_request(self):
        """Does the request dispatching.  Matches the URL and returns the
        return value of the view or error handler.  This does not have to
        be a response object.  In order to convert the return value to a
        proper response object, call :func:`make_response`.
        """
        try:
            endpoint, values = self.match_request()
            return self.view_functions[endpoint](**values)
        except HTTPException, e:
            handler = self.error_handlers.get(e.code)
            if handler is None:
                return e
            return handler(e)
        except Exception, e:
            handler = self.error_handlers.get(500)
            if self.debug or handler is None:
                raise
            return handler(e)

来源http://sfau.lt/b5rSPQ 感谢原作者_zhao与segmentfault平台

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值