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。
- 将
ctx和app_ctx入栈(分别通过自己的LocalStack对象将其放入到Local中,后面介绍) - 执行所有的before_request函数
- 执行视图函数
- 执行所有after_request函数(session加密放到cookie中)
- 销毁ctx和app_ctx
浙公网安备 33010602011771号