上篇笔记有了wsgi_app
的概念。
wsgi_app
用来连接WSGI server
和应用程序。
主要功能是调用各种函数来处理请求,然后将处理结果返回给服务器。
这篇笔记来看wsgi_app
中具体处理请求(目前就是访问一个网址)的过程。
Flask 从客户端收到请求时,要让视图函数能访问一些对象,才能处理请求。例如视图函数需要知道它执行情况的请求信息(请求的 url,参数,方法等)以及应用信息(应用中初始化的数据库等),才能够正确运行。
Flask 中使用上下文把这些信息变成类似全局变量的东西。
从wsgi_app()
函数内部第一句ctx = self.request_context(environ)
开始看:
- ctx for context,这里就是上下文的意思。flask中上下文分为应用上下文和请求上下文。
- 使用
request_context()
方法(RequestContext类)来实例化一个ctx对象。可以看出,ctx使用栈(stack)结构来存储信息。 environ
由WSGI server
提供。- 实例化ctx 后,
wsgi_app()
中接着使用了ctx.push()
,把上下文内容进行压栈,放在了栈顶。 - 上面语句产生的所用是生成了一个
request
请求对象以及包含请求信息在内的request context。
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
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)
def request_context(self, environ):
return RequestContext(self, environ)
到RequestContext()
中看完整的push()
:
_request_ctx_stack = LocalStack()
,_request_ctx_stack
为实例化的LocalStack()
类。top = _request_ctx_stack.top
即将栈顶的元素赋给top,ctx.push()
语句没有传入数据,这里top = None
。- 同样的
app_ctx = None
, 不会执行if 语句之后的内容。 - 关键的
_request_ctx_stack.push(self)
语句,这里self为上下文实例 ctx 本身,将上下文内容压缩到栈中,放在栈顶。
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 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)
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
# 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'))
有了上下文的概念,继续看
wsgi_app()
:
response = self.full_dispatch_request()
,将full_dispatch_request()
方法的返回内容赋值给response
,最后返回到服务器。
full_dispatch_request()
主要功能就是分发请求:
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
"""
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)
return self.finalize_request(rv)
__init__函数中设置了self._got_first_request = False
,try_trigger_before_first_request_functions()
的最后一句设置了self._got_first_request = True
,即got_first_request(self)
中描述的 This attribute is set to True
if the application started handling the first request.
表明现在开始处理程序了。
def try_trigger_before_first_request_functions(self):
"""Called before each request and will ensure that it triggers
the :attr:`before_first_request_funcs` and only exactly once per
application instance (which means process usually).
:internal:
"""
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
for func in self.before_first_request_funcs:
func()
self._got_first_request = True
def got_first_request(self):
"""This attribute is set to ``True`` if the application started
handling the first request.
.. versionadded:: 0.8
"""
return self._got_first_request
接着看
full_dispatch_request()
,
try
中的
request_started.send(self)
:
根据介绍,
request_started
是一个信号名,表明开始处理请求,最后返回信号名和信号内容。
send(self)
方法将请求返回信号本身。
request_started = _signals.signal('request-started')
_signals = Namespace()
class Namespace(object):
def signal(self, name, doc=None):
return _FakeSignal(name, doc)
class _FakeSignal(object):
"""If blinker is unavailable, create a fake class with the same
interface that allows sending of signals but will fail with an
error on anything else. Instead of doing anything on send, it
will just ignore the arguments and do nothing instead.
"""
def __init__(self, name, doc=None):
self.name = name
self.__doc__ = doc
def _fail(self, *args, **kwargs):
raise RuntimeError('signalling support is unavailable '
'because the blinker library is '
'not installed.')
send = lambda *a, **kw: None
connect = disconnect = has_receivers_for = receivers_for = \
temporarily_connected_to = connected_to = _fail
del _fail
full_dispatch_request
接下来运行 rv = self.preprocess_request()
,对请求进行预处理。
在__init__
中设置了self.url_value_preprocessors = {}
和self.before_request_funcs = {}
,这里啥也没运行。只有在运行url_value_preprocessor
和before_request()
才会运行之后的代码。
def preprocess_request(self):
"""Called before the actual request dispatching and will
call each :meth:`before_request` decorated function, passing no
arguments.
If any of these functions returns a value, it's handled as
if it was the return value from the view and further
request handling is stopped.
This also triggers the :meth:`url_value_preprocessor` functions before
the actual :meth:`before_request` functions are called.
"""
bp = _request_ctx_stack.top.request.blueprint
funcs = self.url_value_preprocessors.get(None, ())
if bp is not None and bp in self.url_value_preprocessors:
funcs = chain(funcs, self.url_value_preprocessors[bp])
for func in funcs:
func(request.endpoint, request.view_args)
funcs = self.before_request_funcs.get(None, ())
if bp is not None and bp in self.before_request_funcs:
funcs = chain(funcs, self.before_request_funcs[bp])
for func in funcs:
rv = func()
if rv is not None:
return rv
预处理之后
rv=None
, 开始运行
rv = self.dispatch_request()
:
-
_request_ctx_stack
,即请求上下文对象,在运行wsgi_app()
时将请求上下文放在了栈顶。 -
_request_ctx_stack.top.request
保存着当前请求信息。在每次请求过来的时候,flask 会把当前请求的信息保存进去,这样我们就能在整个请求处理过程中使用它。 - 主要看函数最后返回值,返回了
view_functions
,在__init__
中设置了self.view_functions = {}
,空字典。只有运行add_url_rule()
时才会将URL和视图函数的映射关系添加到字典中。 - 即
dispatch_request()
处理结果是得到了一个 URL endpoint (例如‘/’,’/index’)和视图函数的映射关系字典,full_dispatch_request()
中将处理结果赋值给rv
。
def dispatch_request(self):
"""Does the request dispatching. Matches the URL and returns the return value of the view or error handler.
"""
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule = req.url_rule
# if we provide automatic options for this URL and the
# request came with the OPTIONS method, reply automatically
if getattr(rule, 'provide_automatic_options', False) \
and req.method == 'OPTIONS':
return self.make_default_options_response()
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args)
再回到
full_dispatch_request()
,函数最后返回了
self.finalize_request(rv)
,即使用
finalize_request
方法来处理
rv
。
response = self.make_response(rv)
,make_response(rv)
介绍: Converts the return value from a view function to a real response object that is an instance of : attr: response_class. 将视图函数中的内容变成真正的响应。process_response(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. 请求正常处理之后的 ,结果返回到WSGI server 之前再处理一遍response
。finalize_request()
最后返回response
。- 即
full_dispatch_request
最后返回了response
。 - 再回头看
wsgi_app
,都连接起来了。wsgi_app
最后将response
返回给WSGI server
,WSGI server
将response
返回到客户端,一个请求的处理就完成了。
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.
"""
response = self.make_response(rv)
try:
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
参考文章: