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 路由封装

  1. 执行@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
  1. 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
  1. 执行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:

  1. 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)
posted @ 2022-08-05 00:46  小苏xx  阅读(878)  评论(0)    收藏  举报