Flask 请求到来阶段
3.请求到来
from flask import Flask
app = Flask(__name__)
app.config.from_object('config.setting')
@app.before_request
def f1():
print("f1")
@app.before_request
def f2():
print("f2")
@app.after_request
def f3(response):
print("f3")
return response
@app.after_request
def f4(response):
print("f4")
return response
@app.route("/index")
def index():
return "Hello World"
# 只在第一次请求到来的时候最先执行,之后不在执行。
@app.before_first_request
def f5():
print("f5")
if __name__ == '__main__':
app.run()
所写代码,实例化对象,启动程序并加上对应的装饰器。
3.1 实例化对象
class Flask:
config_class = Config
def __init__(
self,
import_name: str,
static_url_path: t.Optional[str] = None,
static_folder: t.Optional[t.Union[str, os.PathLike]] = "static",
static_host: t.Optional[str] = None,
host_matching: bool = False,
subdomain_matching: bool = False,
template_folder: t.Optional[str] = "templates",
instance_path: t.Optional[str] = None,
instance_relative_config: bool = False,
root_path: t.Optional[str] = None,
):
self.config = self.make_config(instance_relative_config)
self.before_first_request_funcs: t.List[BeforeFirstRequestCallable] = []
self.url_map = self.url_map_class()
3.2 加载配置文件
# 执行的是当前对象的config属性的方法
app.config.from_object('config.setting')
# app 相当于 self
self.config = self.make_config(instance_relative_config)
def make_config(self, instance_relative: bool = False) -> Config:
"""Used to create the config attribute by the Flask constructor.
The `instance_relative` parameter is passed in from the constructor
of Flask (there named `instance_relative_config`) and indicates if
the config should be relative to the instance path or the root path
of the application.
.. versionadded:: 0.8
"""
# 返回值是 Config 类型的对象
root_path = self.root_path
if instance_relative:
root_path = self.instance_path
defaults = dict(self.default_config)
defaults["ENV"] = get_env()
defaults["DEBUG"] = get_debug_flag()
# config_class 为Flask的类属性,可以供对象调用。此处加括号即为实例化相关的对象
return self.config_class(root_path, defaults)
Config 类
class Config:
def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None:
# 默认配置文件的字典或者是空字典
dict.__init__(self, defaults or {})
self.root_path = root_path
实力化Config返回一个字典,其中存储了相关的配置项。
3.3 请求前的装饰器
@setupmethod
def before_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable:
# 将被装饰的函数当做参数传入
"""Register a function to run before each request.
For example, this can be used to open a database connection, or
to load the logged in user from the session.
.. code-block:: python
@app.before_request
def load_user():
if "user_id" in session:
g.user = db.session.get(session["user_id"])
The function will be called without any arguments. If it returns
a non-``None`` value, the value is handled as if it was the
return value from the view, and further request handling is
stopped.
"""
self.before_request_funcs.setdefault(None, []).append(f)
return f
补充:视图函数执行完成之后执行的after_request与这里的原理类似。
3.4 执行视图函数
相关博客:https://www.cnblogs.com/Blogwj123/p/16522441.html
3.5 程序启动
def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app`, which can be
wrapped to apply middleware.
"""
return self.wsgi_app(environ, start_response)
执行app.__call__方法。
def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
"""The actual WSGI application. This is not implemented in
:meth:`__call__` so that middlewares can be applied without
losing a reference to the app object. Instead of doing this::
app = MyMiddleware(app)
It's a better idea to do this instead::
app.wsgi_app = MyMiddleware(app.wsgi_app)
Then you still have the original application object around and
can continue to call methods on it.
.. versionchanged:: 0.7
Teardown events for the request and app contexts are called
even if an unhandled error occurs. Other events may not be
called depending on when an error occurs during dispatch.
See :ref:`callbacks-and-errors`.
:param environ: A WSGI environment.
:param start_response: A callable accepting a status code,
a list of headers, and an optional exception context to
start the response.
"""
# 传入的请求环境 environ。实例化上下文的对象
ctx = self.request_context(environ)
error: t.Optional[BaseException] = None
try:
try:
# 执行对象的 push().将上下文进行入栈
ctx.push()
# 3.6 full_dispatch_request,执行视图函数等相关的操作
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error) # 出现异常时销毁上下文,
request_context 函数。
def request_context(self, environ: dict) -> RequestContext:
"""Create a :class:`~flask.ctx.RequestContext` representing a
WSGI environment. Use a ``with`` block to push the context,
which will make :data:`request` point at this request.
See :doc:`/reqcontext`.
Typically you should not call this from your own code. A request
context is automatically pushed by the :meth:`wsgi_app` when
handling a request. Use :meth:`test_request_context` to create
an environment and context instead of this method.
:param environ: a WSGI environment
"""
# 返回responseContext对象,对应的类如下所示
return RequestContext(self, environ)
RequestContext 类
class RequestContext:
def __init__(
self,
app: "Flask",
environ: dict,
request: t.Optional["Request"] = None,
session: t.Optional["SessionMixin"] = None,
) -> None:
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = None
try:
self.url_adapter = app.create_url_adapter(self.request)
except HTTPException as e:
self.request.routing_exception = e
self.flashes = None
self.session = session
self._implicit_app_ctx_stack: t.List[t.Optional["AppContext"]] = []
self.preserved = False
self._preserved_exc = None
self._after_request_functions: t.List[AfterRequestCallable] = []
@property
def g(self) -> AppContext:
return _app_ctx_stack.top.g # 返回应用上下文中的 g
@g.setter
def g(self, value: AppContext) -> None:
_app_ctx_stack.top.g = value # 将 g 写入应用上下文中
def copy(self) -> "RequestContext":
return self.__class__(
self.app,
environ=self.request.environ,
request=self.request,
session=self.session,
)
def match_request(self) -> None:
try:
result = self.url_adapter.match(return_rule=True)
self.request.url_rule, self.request.view_args = result
except HTTPException as e:
self.request.routing_exception = e
# ctx.push()直接调用此方法
def push(self) -> None:
# _app_ctx_stack = LocalStack()
top = _request_ctx_stack.top
if top is not None and top.preserved: # 不为空的时候弹出对应的元素
top.pop(top._preserved_exc)
# 取出对应的存储元素,此时由于LocalStack 的线程隔离作用,线程之间是安全的
app_ctx = _app_ctx_stack.top
# 检查请求的应用上下文存储的值是否存在.
if app_ctx is None or app_ctx.app != self.app:
# 存在 且不是None 和 原来的值
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)
# _request_ctx_stack = LocalStack()请求上下文中的push
_request_ctx_stack.push(self)# 将当前对象放入请求上下文中
if self.session is None:
session_interface = self.app.session_interface
self.session = session_interface.open_session(self.app, self.request)
if self.session is None:
self.session=session_interface.make_null_session(self.app)
if self.url_adapter is not None:
self.match_request()
def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None:
app_ctx = self._implicit_app_ctx_stack.pop()
clear_request = False
try:
if not self._implicit_app_ctx_stack:
self.preserved = False
self._preserved_exc = None
if exc is _sentinel:
exc = sys.exc_info()[1]
self.app.do_teardown_request(exc)
request_close = getattr(self.request, "close", None)
if request_close is not None:
request_close()
clear_request = True
finally:
rv = _request_ctx_stack.pop() # 销毁请求上下文。
if clear_request:
rv.request.environ["werkzeug.request"] = None
if app_ctx is not None:
app_ctx.pop(exc) # 弹出对应的元素,销毁应用上下文
assert (
rv is self
), f"Popped wrong request context. ({rv!r} instead of {self!r})"
def auto_pop(self, exc: t.Optional[BaseException]) -> None:
if self.request.environ.get("flask._preserve_context") or (
exc is not None and self.app.preserve_context_on_exception
):
self.preserved = True
self._preserved_exc = exc # type: ignore
else:
self.pop(exc)
def __enter__(self) -> "RequestContext":
# 支持上下文管理,入口
self.push()
return self
def __exit__(
self, exc_type: type, exc_value: BaseException, tb: TracebackType
) -> None:
# 支持上下文管理,出口
self.auto_pop(exc_value)
def __repr__(self) -> str:
return (
f"<{type(self).__name__} {self.request.url!r}"
f" [{self.request.method}] of {self.app.name}>"
)
# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
# 这两个对象已经被创建,其他地方只使用的相关对象的,是单例模式。
localstack 的函数
@property
def top(self) -> t.Any:
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-1] # 取出栈顶的元素
except (AttributeError, IndexError):
return None
flask 的两个核心上下文对象,主要是有应用上下文与请求上下文结合使用,其中使用了线程安全隔离的技术。
3.6 视图函数中的源码。
def full_dispatch_request(self) -> Response:
"""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
"""
# 执行特殊的装饰器 @app.before_first_request
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
# 执行相关的请求信息
rv = self.preprocess_request()
if rv is None:
# 请求上下文中取出request 匹配 路由信息,去view_funcs中获取相关视图函数信息加括号执行
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
根据 endpoint查找视图函数
def preprocess_request(self) -> t.Optional[ResponseReturnValue]:
"""Called before the request is dispatched. Calls
:attr:`url_value_preprocessors` registered with the app and the
current blueprint (if any). Then calls :attr:`before_request_funcs`
registered with the app and the blueprint.
If any :meth:`before_request` handler returns a non-None value, the
value is handled as if it was the return value from the view, and
further request handling is stopped.
"""
names = (None, *reversed(request.blueprints))
for name in names:
if name in self.url_value_preprocessors:
for url_func in self.url_value_preprocessors[name]:
# 传入参数为 endpoint 和 视图参数
url_func(request.endpoint, request.view_args)
for name in names:
if name in self.before_request_funcs:
# 检查是否存在 before——request函数需要执行
for before_func in self.before_request_funcs[name]:
rv = self.ensure_sync(before_func)()
if rv is not None:
return rv
return None
执行视图函数
def dispatch_request(self) -> ResponseReturnValue:
"""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`.
.. versionchanged:: 0.7
This no longer does the exception handling, this code was
moved to the new :meth:`full_dispatch_request`.
"""
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.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
执行完之后,按照wsgi_app中的流程销毁上下文对象。

浙公网安备 33010602011771号