02. Flsak源码分析之【请求流程】

上篇说到由app.run()启动,经__call__()方法,调用了wsgi_app,如下:

def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
    # 1.创建RequestContext对象
    ctx = self.request_context(environ)
    error: t.Optional[BaseException] = None
    try:
        try:
            # 2.ctx入栈(内部做了很多事,如创建AppContext对象、等)
            ctx.push() 
            # 3.执行视图函数
            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
        # 4.最后ctx出栈(销毁ctx/app_ctx)
        ctx.auto_pop(error)

 

1.创建RequestContext对象

def request_context(self, environ: dict) -> RequestContext:
	# 这里的self是app对象;将app对象当做第一个参数,实例化一个RequestContext对象
    return RequestContext(self, environ)  

ctx.py:RequestContext如下:

class RequestContext:

    def __init__(self,app,environ,request=None,session=None):
        self.app = app
        if request is None: # request如果为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  # 注意这里的session最开始为None
        self._implicit_app_ctx_stack: t.List[t.Optional["AppContext"]] = []
        self.preserved = False
        self._preserved_exc = None
        self._after_request_functions: t.List[AfterRequestCallable] = []

在RequestContext对象中封装了很多数据,其中最主要的就是封装了:

  • request对象,request = Request(environ) ,environ是请求的原始数据
  • session = None

 

2.创建AppContext对象&入栈

ctx是上面RequestContext实例化的对象,有push()方法如下:

def push(self):
    app_ctx = _app_ctx_stack.top
    if app_ctx is None or app_ctx.app != self.app:
        # 创建AppContext对象,里面封装了app对象和g
        app_ctx = self.app.app_context()  # AppContext()
        # app_ctx入栈
        app_ctx.push()
        self._implicit_app_ctx_stack.append(app_ctx)
    else:
        self._implicit_app_ctx_stack.append(None)

	# 这里_request_ctx_stack是LocalStack()对象,在globals.py中定义的全局对象
    _request_ctx_stack.push(self) # 注意传递了self参数

    # 只有第一次push时,session为None
    if self.session is None:
        # self.app就是我们创建的app
        session_interface = self.app.session_interface
        # 将app对象、request对象放入session
        self.session = session_interface.open_session(self.app, self.request)

        if self.session is None:
            # 如果session仍然为空,则创建一个空session
            self.session = session_interface.make_null_session(self.app)
    if self.url_adapter is not None:
        # 路由匹配
        self.match_request()

这一步做了很多事,概括如下:

  • app_ctx = AppContext() -->创建AppContext对象,里面封装了app对象和g
  • 将app_ctx对象入栈
  • 将ctx对象入栈
  • session赋值
  • 路由匹配

 

3.执行视图函数

def full_dispatch_request(self) -> Response:
    # 执行所有的before_first_request函数
    # 只有启动程序后,第一个请求到来时执行
    self.try_trigger_before_first_request_functions()
    try:
        request_started.send(self) # 发送信号
        # 执行视图之前:执行所有的before_request函数
        rv = self.preprocess_request()
        if rv is None:
            # 执行视图函数
            rv = self.dispatch_request()
    except Exception as e:
        rv = self.handle_user_exception(e)
    # 视图函数执行之后
    # 1.执行所有的after_request
    # 2.保存session
    return self.finalize_request(rv)

这一步的执行内容:

  • 执行before_request函数(初次请求会先执行before_first_request
  • 执行视图函数
  • 执行after_request函数,并保存session

这段代码最核心的内容是 dispatch_request,返回的是视图函数的返回结果(比如 hello world 等页面字符串),finalize_request 会把它转换成 Response 对象。

dispatch_request 要做的就是找到我们的视图函数,并返回调用的结果,也就是路由的过程,后面再介绍。

 

4.销毁ctx/app_ctx

不管前面的处理是否发生异常,最终执行ctx.auto_pop(error),把这次请求的ctx对象出栈

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:
        # 最终,出栈,如果没发生错误,这里的exc=None
        self.pop(exc)

调用ctx.pop出栈

def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None:  # type: ignore
    # 将app_ctx出栈
    app_ctx = self._implicit_app_ctx_stack.pop()
    clear_request = False

    try:
        # 如果此时_implicit_app_ctx_stack栈中已经没有元素了,就执行下面代码
        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:
        # 和入栈同理,最终将ctx出栈
        rv = _request_ctx_stack.pop()

至此,整个请求流程就结束了.
 

总结:

  • 创建ctx = RequestContext对象,其内部封装了 Request对象和session数据。
  • 创建app_ctx = AppContext对象,其内部封装了App和g。
  • ctxapp_ctx 入栈(分别通过自己的LocalStack对象将其放入到Local中,后面介绍)
  • 执行所有的before_request函数
  • 执行视图函数
  • 执行所有after_request函数(session加密放到cookie中)
  • 销毁ctx和app_ctx
posted on 2022-06-20 16:30  吃大飞  阅读(37)  评论(0)    收藏  举报