Flask请求生命周期-wsgi_app代码注解

新手上路,小心开车

一、add_url_rule

add_url_rule是Flask框架中注册路由的函数(class App(Scaffold)类方法),源代码注释如下:

    @setupmethod
    def add_url_rule(
        self,
        rule: str,
        endpoint: str | None = None,
        view_func: ft.RouteCallable | None = None,
        provide_automatic_options: bool | None = None,
        **options: t.Any,
    ) -> None:
        """
        endpoint代表视图函数,如果用户没有指定endpoint,则默认是视图函数的__name__属性,即函数 
        名
        """
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)  # type: ignore
        options["endpoint"] = endpoint
        """
        methods表示请求方式列表,如果视图函数没有显示约束请求方式,则默认是GET方式;
        如果包含GET请求方法,但是没有HEAD方式,也会把HEAD方式添加上(Rule类中实现)
        """
        methods = options.pop("methods", None)
        # if the methods are not given and the view_func object knows its
        # methods we can use that instead.  If neither exists, we go with
        # a tuple of only ``GET`` as default.
        if methods is None:
            methods = getattr(view_func, "methods", None) or ("GET",)
        if isinstance(methods, str):
            raise TypeError(
                "Allowed methods must be a list of strings, for"
                ' example: @app.route(..., methods=["POST"])'
            )
        methods = {item.upper() for item in methods}
        # Methods that should always be added
        required_methods: set[str] = set(getattr(view_func, "required_methods", ()))
        
        # starting with Flask 0.8 the view_func object can disable and
        # force-enable the automatic options handling.
        """
        PROVIDE_AUTOMATIC_OPTIONS:表示是否自动添加OPTIONS请求方式,
        要添加OPTIONS请求,要么显示指定OPTIONS请求方法,要么在config字典配置 
        PROVIDE_AUTOMATIC_OPTIONS=True
        """
        if provide_automatic_options is None:
            provide_automatic_options = getattr(
                view_func, "provide_automatic_options", None
            )
        if provide_automatic_options is None:
            if "OPTIONS" not in methods and self.config["PROVIDE_AUTOMATIC_OPTIONS"]:
                provide_automatic_options = True
                required_methods.add("OPTIONS")
            else:
                provide_automatic_options = False
        
        # Add the required methods now.
        methods |= required_methods
        """
        把当前url转化为Rule对象实例,它包含了当前url的路由、请求方式、视图函数等属性
        """
        rule_obj = self.url_rule_class(rule, methods=methods, **options)
        rule_obj.provide_automatic_options = provide_automatic_options  
        """
        所有的Rule对象实例都记录在self.url_map对象中,这是一个Map对象实例
        """
        self.url_map.add(rule_obj)
        """
        所有的视图函数都记录在self.view_functions字典中,因此endpoint不允许重名
        """
        if view_func is not None:
            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}"
                )
            self.view_functions[endpoint] = view_func

二、wsgi_app  

wsgi_app是Flask作为WSGI应用程序的入口(class Flask(App)类方法),源代码注解如下:

    def wsgi_app(
        self, environ: WSGIEnvironment, start_response: StartResponse
    ) -> cabc.Iterable[bytes]:
        """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.
        """

        """
        生成RequestContext实例,将environ字典转化为request对象, ctx.request
        """
        ctx = self.request_context(environ)
        error: BaseException | None = None
        try:
            try:
                """
                ctx.push()主要完成三件事:
                1.生成AppContext实例,使current_app和g对象可用;
                2.加载会话数据,调用session_interface.open_session(),使session对象可以
                3.路由匹配,将请求的路由和flask所有注册路由进行匹配,匹配不成功,则把存储错误 
                  信息存储到request.routing_exception属性中,匹配成功则生成Rule对象(包含 
                   endpoint属性)和路由参数字典,并赋值给request.url_rule和 
                   request.view_args属性。
                """
                ctx.push()
                """
                full_dispatch_request()代码逻辑如下:
                1.发送request_started信号;
                2.先调用所有url_value_preprocessor() 装饰的函数,然后再调用所有 
                  before_request() 装饰的函数;
                3.如果before_request()装饰的函数有任何返回值,则会被当做响应返回,如果没有,则 
                  会调用dispatch_request()方法,找到相应的视图函数执行,并把执行结果返回。
                4.如果上述步骤发生异常,则会调用errorhandler() 装饰器函数处理异常;
                5.最后调用finalize_request()方法,先把第三步返回的结果通过make_response封装成 
                  response对象,然后任何 after_this_request() 装饰的函数都会被调用,再然后任 
                  何 after_request() 装饰的函数都会被调用,最后使用 
                  session_interface.save_session来持久化任何已修改的会话数据,并发送 
                  request_finished 信号。
                """
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                """
                 如果到目前为止的任何步骤引发了一个异常,并且没有被错误处理函数处理,那么现在会被 
                 转换为一个通用的 500 响应,并且got_request_exception 信号被发送。
                """
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            """
            响应对象的状态、头部信息和正文被返回给WSGI服务器。
            """
            return response(environ, start_response)
        finally:
            if "werkzeug.debug.preserve_context" in environ:
                environ["werkzeug.debug.preserve_context"](_cv_app.get())
                environ["werkzeug.debug.preserve_context"](_cv_request.get())

            if error is not None and self.should_ignore_error(error):
                error = None
            """
            pop()方法主要是清理请求上下文和应用上下文
            1.任何 teardown_request() 装饰的函数都被调用,request_tearing_down 
             信号被发送,请求上下文被弹出, request 和 session 不再可用;
            2.任何 teardown_appcontext() 的装饰函数都被调用,appcontext_popped信号被发送,应 
              用上下文被弹出, current_app 和 g 不再可用。
            """
            ctx.pop(error)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值