Flask框架之启动以及请求生命周期全过程源码刨析
Flask框架之启动以及请求生命周期全过程源码刨析
1、项目启动阶段
启动流程
from flask import Flask, request, render_template, jsonify, redirect, url_for, session
# 1.1 实例化Flask对象
app = Flask(__name__)
# 1.2 加载配置文件
app.config.from_object('config.settings')
# 1.3 进入视图前执行的函数
@app.before_request
def f1():
pass
@app.before_request
def f2():
pass
# 1.4 执行完视图后执行的函数
@app.after_request
def f3(response):
return reponse
@app.after_request
def f4(reponse):
return reponse
# 1.5 第一次请求到来时执行的函数,只执行一次
@app.before_first_request
def f5():
pass
# 1.6 路由及视图
@app.route('/login', methods=['GET', 'POST'], endpoint='login')
def login():
pass
def index():
return '首页'
app.add_url_rule('/index', 'index', index)
if __name__ == '__main__':
# 1.7 项目启动
app.run()
1.1 实例化Flask对象,即app=Flask(__ name __)
项目启动准备的首先就是实例化Flask对象,内部封装了许多类变量和实例变量,用来封装的各种对象函数的。
app = Flask(__name__)
# app对象内部封装
class Flask(Scaffold):
config_class = Config # 配置文件的封装
url_rule_class = Rule # 将url和endpoint封装到Rule对象中
url_map_class = Map # 将rule对象封装到Map对象中
def __init__(self):
self.config = self.make_config(instance_relative_config) # 封装配置文件
self.before_request_funcs: {} # 用于封装执行视图之前执行的函数
self.after_request_funcs:{} # 用于封装执行视图之后执行的函数
self.before_first_request_funcs: []
self.url_map = self.url_map_class() # map对象,内部封装rule对象
self.view_functions: {} # 用于封装视图函数,以endpoint为键,视图函数为值
1.2 加载配置文件 app.config.from_object('config.settings')
接下来是加载配置文件,配置文件的路径是通过字符串的形式传递,内部会通过 '.' 进行分割然后获取配置文件的所有数据,并将其封装到一个字典中
app.config.from_object('config.settings')
# 1.app.config
self.config = self.make_config(instance_relative_config)
# 2.self.make_config(instance_relative_config)
def make_config(self, instance_relative: bool = False) -> Config:
return self.config_class(root_path, defaults)
# 3.由self.config_class(root_path, defaults) 找到类变量,相当于实例化Config类,并封装到app.config对象中
config_class = Config
# 4.调用from_object方法
class Config(dict):
def from_object(self, obj): # obj就是配置文件的字符串路径
if isinstance(obj, str):
obj = import_string(obj) # 获取配置文件中的所有配置
for key in dir(obj): # 将配置文件中的所有配置放入一个空列表[]中,进行遍历
if key.isupper(): # 配置文件中的变量名必须为大写
self[key] = getattr(obj, key) # 将配置文件中的配置以变量名为key,值为vlaue存入一 个字典中
1.3 封装app.before_request
可以通过装饰器的形式 @app.before_request 将函数进行装饰,这样就会将函数封装到 self.before_request_funcs 的字典中,字典以None为键,列表为值,函数存储在列表中,就可以实现在执行视图之前执行这些函数。
@app.before_request
def f1():
pass
@app.before_request
def f2():
pass
# 调用before_request方法,将所有函数封装到实例变量 before_request_funcs 字典中
class Scaffold:
def before_request(self, f):
"""
以None为键,[]为值,即:
self.before_request_funcs: {
None:[f1,f2]
}
"""
self.before_request_funcs.setdefault(None, []).append(f)
return f
1.4 封装app.after_request
可以通过装饰器的形式 @app.after_request将函数进行装饰,这样就会将函数封装到 self.after_request_funcs的字典中,字典以None为键,列表为值,函数存储在列表中,就可以实现在执行视图之后执行这些函数。
@app.after_request
def f3():
pass
@app.after_request
def f4():
pass
# 调用after_request方法,将所有函数封装到实例变量 after_request_funcs 字典中
class Scaffold:
def after_request(self, f):
"""
以None为键,[]为值,即:
self.after_request_funcs: {
None:[f3,f4]
}
"""
self.after_request_funcs.setdefault(None, []).append(f)
return f
1.5 封装app.before_first_request
还可以通过装饰器@app.before_first_request对函数进行装饰,也是在视图函数之前执行,在before_request之前执行,他是将函数封装到self.before_first_request_funcs的列表中,不过只会在第一个请求进来时执行,后面的请求进来不再执行,除非项目重启。
@app.before_first_request
def f5():
pass
# 调用before_first_request方法,将所有函数封装到实例变量 before_first_request_funcs 列表中
class Flask(Scaffold);
def before_first_request(self, f) :
self.before_first_request_funcs.append(f) # 将函数存放到列表中
return f
1.6 路由封装
- 执行@app.route(url) 的得到返回值,返回decorator方法
class Scaffold: # Flask类的父类
def route(self, rule: str, **options: t.Any) -> t.Callable[[F], F]:
def decorator(f: F) -> F:
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
- decorator中,执行self.add_url_rule(rule, endpoint, f, **options)
class Flask(Scaffold):
@setupmethod
def add_url_rule(
self,
rule: str,
endpoint: t.Optional[str] = None,
view_func: t.Optional[t.Callable] = None,
) -> None:
# 中间部分主要是判断是否有 endpoint 以及 methods ,然后进行一些处理
rule = self.url_rule_class(rule, methods=methods, **options)
self.url_map.add(rule)
if view_func is not None:
# 从self.view_functions: {} 根据endpoint获取函数,判断是否存在
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError(
"View function mapping is overwriting an existing"
f" endpoint function: {endpoint}"
)
# 以endpoin为键,视图函数为值,封装到self.view_functions: {}字典中
self.view_functions[endpoint] = view_func
- 执行rule = self.url_rule_class(rule, methods=methods, **options),得到的是rule对象
self.url_rule_class = Rule
# Rule对象对数据进行封装,封装了url,methods,endpoint
class Rule(RuleFactory):
def __init__(
self,
string: str, # url
methods: t.Optional[t.Iterable[str]] = None,
endpoint: t.Optional[str] = None,
...
) -> None:
- self.url_map.add(rule),将所有的rule对象封装到map对象中
self.url_map = self.url_map_class()
self.url_map_class = Map
# 将rule对象封装到map对象中
class Map:
def __init__(
self,
rules: t.Optional[t.Iterable[RuleFactory]] = None,
...)
1.7 执行app.run()
项目启动的最后一步就是执行run方法了,内部是通过调用werkzeug的run_simple方法。
if __name__ == '__main__':
app.run()
# 执行run()方法,内部主要调用了werkzeug的run_simple方法
class Flask(Scaffold):
def run(self):
from werkzeug.serving import run_simple
try:
run_simple(t.cast(str, host), port, self, **options)
扩展、CBV源码实现
1、先执行as_view(),返回view方法
@classmethod
def as_view(
cls, name: str, *class_args: t.Any, **class_kwargs: t.Any
) -> t.Callable:
def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
self = view.view_class(*class_args, **class_kwargs) # type: ignore
return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
return view
2、请求进来执行View中的view方法
def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
self = view.view_class(*class_args, **class_kwargs) # type: ignore
return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
3、接着执行MethodView中dispatch_request方法,通过反射找到当前请求的视图方法
class MethodView(View, metaclass=MethodViewType):
def dispatch_request(self, *args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
meth = getattr(self, request.method.lower(), None)
if meth is None and request.method == "HEAD":
meth = getattr(self, "get", None)
assert meth is not None, f"Unimplemented method {request.method!r}"
return current_app.ensure_sync(meth)(*args, **kwargs)
2. 请求进来阶段
请求进来首先执行的是app.__ call__方法
请求进来后,会执行run_simple中的第三个参数self也就是app对象会加括号,然后执行app.__ call__方法,call方法又会去执行wsgi_app方法,wsgi_app中主要分为四大部分,详细请往下看:
class Flask(Scaffold):
def run(self):
from werkzeug.serving import run_simple
try:
# 请求进来,先执行第三个参数 self.__call__反方法,也就是Flask的__call__方法
run_simple(t.cast(str, host), port, self, **options)
# 执行 app.__call__ 方法
def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
return self.wsgi_app(environ, start_response)
# 执行 self.wsgi_app(environ, start_response)
def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
# 2.1 实例化RequestContext对象,封装request和session
ctx = self.request_context(environ)
error: t.Optional[BaseException] = None
try:
try:
# 2.2 执行RequestContext对象的push方法
ctx.push()
# 2.3 执行Flask对象的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
# 2.4 请求结束,销毁ctx对象和app_ctx对象
ctx.auto_pop(error)
2.1 执行 ctx = self.request_context(environ) ,得到RequestContext对象
在wsgi_app方法中,首先就是执行self.request_context(environ) ,返回的是一个RequestContext对象,内部封装了request对象和session对象。
# 1.实例化RequestContext类,得到ctx对象,将request和session封装到RequestContext对象中
ctx = self.request_context(environ)
# 2.
class Flask:
def request_context(self, environ: dict) -> RequestContext:
return RequestContext(self, environ)
# 3.实例化先执行__init__方法,ctx对象中封装了app,request,session
class RequestContext:
def __init__(
self,
app: "Flask",
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
self.session = session
2.2 执行 ctx.push(),相当于执行了RequestContext类的 push 方法
接下来是执行RequestContext对象的push方法,其内部由主要做了以下几件事
class RequestContext:
def push(self) -> None:
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:
# 2.2.1 通过ctx对象获取到app对象,执行app对象的app_context方法,得到AppContext对象
app_ctx = self.app.app_context()
# 2.2.2 执行AppContext对象的push方法
app_ctx.push()
self._implicit_app_ctx_stack.append(app_ctx)
else:
self._implicit_app_ctx_stack.append(None)
# 2.2.5 _request_ctx_stack是LocalStack()的一个实例对象
_request_ctx_stack.push(self)
# 2.2.7 找到ctx对象中的session,给session赋值
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)
# 2.2.8 匹配路由
if self.url_adapter is not None:
self.match_request()
2.2.1 执行 app_ctx = self.app.app_context()
在ctx.push方法中,首先就是通过实例化AppContext类得到app_ctx对象,内部封装了app对象和g对象
# 返回AppContext对象,内部封装了app对象,g对象
class Flask:
def app_context(self) -> AppContext:
return AppContext(self)
# 实例化AppContext,执行__init__方法
class AppContext:
def __init__(self, app: "Flask") -> None:
self.app = app
self.g = app.app_ctx_globals_class()
2.2.2 继续往下执行 app_ctx.push()
然后是执行AppContext对象的push方法,其内部又执行了_app_ctx_stack = LocalStack() 的push方法
class AppContext:
def push(self) -> None:
#2.2.3 _app_ctx_stack是LocalStack()实例化的对象,相当于执行LocalStack()的push方法
_app_ctx_stack.push(self)
2.2.3 往下走 _app_ctx_stack.push(self)
# _app_ctx_stack = LocalStack()
# 实例化得到的是一个Local对象
class LocalStack:
def __init__(self) -> None:
self._local = Local()
def push(self, obj): # obj就是AppContext对象即app_ctx,内部封装了app和g
rv = getattr(self._local, "stack", []).copy()
rv.append(obj)
# 2.2.4 执行local对象的__setattr__方法
self._local.stack = rv
return rv
# 实例化Local对象,其内部维护了一个字典_storage = {}
class Local:
__slots__ = ("_storage",)
def __init__(self) -> None:
object.__setattr__(self, "_storage", ContextVar("local_storage"))
2.2.4 _app_ctx_stack 中 Local对象内部封装
_storage = {
# 根据每一个线程id为键
'1111':{'stack':[app_ctx,]} # app_ctx对象中封装了app对象和g
}
2.2.5 RequestContext类的 push 方法中继续往下执行 _request_ctx_stack.push(self)
# _request_ctx_stack = LocalStack()
# 实例化得到的是一个Local对象
class LocalStack:
def __init__(self) -> None:
self._local = Local()
def push(self, obj): # obj就是RequestContext对象即ctx,内部封装了request和session
rv = getattr(self._local, "stack", []).copy()
rv.append(obj)
# 2.2.4 执行local对象的__setattr__方法
self._local.stack = rv
return rv
# 实例化Local对象,其内部维护了一个字典_storage = {}
class Local:
__slots__ = ("_storage",)
def __init__(self) -> None:
object.__setattr__(self, "_storage", ContextVar("local_storage"))
2.2.6 _request_ctx_stack 中 Local对象内部封装
_storage = {
# 根据每一个线程id为键
'1111':{'stack':[ctx,]} # ctx对象中封装了request和session
}
2.3 根据__ call__方法的wsgi_app方法中 继续往下走,执行response = self.full_dispatch_request()
这里主要做了四件事,按照顺序分别是执行before_first_request 中的函数,执行 before_request 中的函数,执行视图函数,执行 after_request 中的函数,并且保存session
class Flask:
def full_dispatch_request(self) -> Response:
# 2.3.1 执行 before_first_request 中的函数
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
# 2.3.2 执行 before_request 中的函数
rv = self.preprocess_request()
if rv is None:
# 2.3.3 执行视图函数
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
# 2.3.4 执行 after_request 中的函数,并且保存session
return self.finalize_request(rv)
2.3.1 执行before_first_request中的函数, self.try_trigger_before_first_request_functions()
首先是获取项目启动阶段封装在app对象self.before_first_request_funcs列表中的before_first_request函数,通过遍历执行,第一次执行完后,后面不会再执行这些函数。
class Flask:
def try_trigger_before_first_request_functions(self):
# self._got_first_request = False 默认为False
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
# 获取项目启动时封装的before_first_request_funcs,并进行遍历执行
for func in self.before_first_request_funcs:
self.ensure_sync(func)()
# 只有第一次请求进来时会执行,执行完给_got_first_request赋值为True,再次进来时直接return
self._got_first_request = True
2.3.2 执行before_request中的函数,rv = self.preprocess_request()
然后是获取项目启动阶段封装在app对象self.before_request_funcs字典中的before_request函数,通过遍历执行,每次请求到来视图函数之前都会执行,可以用来做登录认证等。
class Flask:
def preprocess_request(self):
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]:
url_func(request.endpoint, request.view_args)
for name in names:
if name in self.before_request_funcs:
# 从app对象中获取所有的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
2.3.3 执行视图函数,rv = self.dispatch_request()
然后就是视图函数了,从Local对象中获取ctx对象,再通过ctx对象获取request对象来获取rule对象,通过rule对象中封装的endpoint,从self.view_functions中获取对应视图然后执行。
class Flask:
def dispatch_request(self) -> ResponseReturnValue:
# 从local对象中获取到ctx对象,获取ctx对象中封装的request对象
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
# 获取当前的路由对象,里面封装了url,endpoint,methods
rule = req.url_rule
if (
getattr(rule, "provide_automatic_options", False)
and req.method == "OPTIONS"
):
return self.make_default_options_response()
# 以endpoint为键,从view_functions中获取对应的视图函数并执行,然后返回
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
2.3.4 执行after_request中的函数,并保存session,self.finalize_request(rv)
最后就是获取项目启动阶段封装在app对象self.after_request_funcs字典中的after_request函数,通过遍历执行,每次请求到来视图函数之后都会执行,然后是保存session。
class Flask:
def finalize_request(
self,
rv: t.Union[ResponseReturnValue, HTTPException],
from_error_handler: bool = False,
):
response = self.make_response(rv)
try:
# 执行after_request中的函数,并保存session
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
# 执行 response = self.process_response(response)
class Flask:
def process_response(self, response: Response):
# 从local对象中获取ctx对象
ctx = _request_ctx_stack.top
for func in ctx._after_request_functions:
response = self.ensure_sync(func)(response)
for name in chain(request.blueprints, (None,)):
if name in self.after_request_funcs:
# 获取所有after_request中的函数,并进行反转,然后执行
for func in reversed(self.after_request_funcs[name]):
response = self.ensure_sync(func)(response)
# 保存session
if not self.session_interface.is_null_session(ctx.session):
self.session_interface.save_session(self, ctx.session, response)
return response
2.4 请求结束,销毁ctx对象和app_ctx对象,ctx.auto_pop(error)
wsgi_app方法中最后一步就是将本次请求的ctx对象和app_ctx对象进行销毁
class RequestContext:
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)
# 执行self.pop(exc)
class RequestContext:
def pop(self, exc: t.Optional[BaseException] = _sentinel):
# 从_implicit_app_ctx_stack列表中获取app_ctx对象
app_ctx = self._implicit_app_ctx_stack.pop()
try:
pass
finally:
# 将ctx对象销毁
rv = _request_ctx_stack.pop()
if app_ctx is not None:
# 将app_ctx 销毁
app_ctx.pop(exc)

浙公网安备 33010602011771号