1.为了避免大良可有可无的参数把视图函数弄得一团糟,Flask使用上下文临时把某些对象变为全局可访问。
上下文(context)分为程序上下文(application)和请求(request context)上下文。
变量名 | 上下文 | 说 明 |
---|---|---|
current_app | 程序上下文 | 当前激活程序的程序实例 |
g | 程序上下文 | 处理请求时用作临时存储的对象。每次请求都会重设这个变量 |
request | 请求上下文 | 请求对象,封装了客户端发出的HTTP请求中的内容 |
session | 请求上下文 | 用户会话,用于存储请求之间需要“记住”的值的词典 |
程序上下文被推送后,就可以在线程中使用current_app和g变量。同样,请求上下文被推送后,就可以使用request和session变量。
这里需要通俗地解释一下application context与request context:
- application 指的就是当你调用
app = Flask(__name__)
创建的这个对象app; - request 指的是每次http请求发生时,
WSGI server
(比如gunicorn)调用Flask.__call__()
之后,在Flask对象内部创建的Request
对象; - application 表示用于响应WSGI请求的应用本身,request 表示每次http请求;
- 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关联起来
总结
app = Flask(name)创建了application,
这个application对应的上下文,就是application contextFlask每响应一个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平台