Flask 启动流程
注意:为了阅读方便,删除了注释和不相干的部分。
应用启动的代码是 app.run()
,这个方法的代码如下:
def run(self, host=None, port=None, debug=None, **options):
"""Runs the application on a local development server."""
from werkzeug.serving import run_simple
...
# 调用 werkzeug.serving 模块的 run_simple 函数,传入收到的参数
# 注意第三个参数传进去的是 self,也就是要执行的 web application
try:
run_simple(host, port, self, **options)
finally:
self._got_first_request = False
**
调用 werkzeug
的 run_simple
。需要注意的是:run_simple
的第三个参数是 self
,也就是我们创建的 Flask()
app。因为 WSGI server 不是本文的重点,现在只需要知道它的功能就行:监听在指定的端口,收到 HTTP 请求的时候解析为 WSGI 格式,然后调用 app
去执行处理的逻辑。
对应的执行逻辑在 werkzeug.serving:WSGIRequestHandler
的 run_wsgi
中有这么一段代码:
def execute(app):
application_iter = app(environ, start_response)
try:
for data in application_iter:
write(data)
if not headers_sent:
write(b'')
finally:
if hasattr(application_iter, 'close'):
application_iter.close()
application_iter = None
可以看到 application_iter = app(environ, start_response)
就是处理请求并返回结果的地方。
要调用 app
实例,需要通过 __call__
方法将 Flask 对象变为可调用,以便 WSGI 服务器调用
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
实际的 WSGI 是通过 wsgi_app
函数实现的,通过 wsgi_app
转发 web 服务器的请求
def wsgi_app(self, environ, start_response):
# 创建请求上下文
ctx = self.request_context(environ)
error = None
try:
try:
# 把请求上下文压栈
ctx.push()
# 正确的请求处理路径,会通过路由找到对应的处理函数
response = self.full_dispatch_request()
except Exception as e:
# 错误处理,默认是 InternalServerError 错误处理函数,客户端会看到服务器 500 异常
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
# 不管处理是否发生异常,都需要把栈中的请求 pop 出来
ctx.auto_pop(error)
根据 environ
生成请求上下文,请求上下文内部工作如同一个栈,栈顶是当前活动的请求。 处理请求前,先请求上下文压栈,请求处理完成后再移出栈。在出栈时,应用的 teardown_request() 函数也会被执行。
通过 full_dsipatch_request
函数分发请求并对请求进行预处理和后处理,同时捕获HTTP 异常并处理,full_dsipatch_request
的代码如下:
def full_dispatch_request(self):
"""Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error handling.
"""
self.try_trigger_before_first_request_functions()
try:
request_started.send(self) # 发送请求进入信号
rv = self.preprocess_request() # 预处理请求
if rv is None:
rv = self.dispatch_request() # 如果预处理未返回处理结果,则进一步处理请求,并返回处理结果
except Exception as e:
rv = self.handle_user_exception(e) # 处理异常
return self.finalize_request(rv) # 生成 Response 对象,并后处理请求
最核心的内容是通过 dispatch_request
处理请求并返回结果(通过 finalize_request
转换成 Response
对象),加上请求的 hooks 处理和错误处理的内容。
通过 preprocess_request
和 finalize_request
分别在处理请求之前和处理之后添加了很多 hooks 。这些 hooks 包括:
- 第一次请求处理之前的 hook 函数,通过
before_first_request
定义 - 每个请求处理之前的 hook 函数,通过
before_request
定义 - 每个请求正常处理之后的 hook 函数,通过
after_request
定义 - 不管请求是否异常都要执行的
teardown_request
hook 函数
dispatch_request
要做的就是找到对应的处理函数,并返回调用的结果,也就是路由的过程
**
def finalize_request(self, rv, from_error_handler=False):
response = self.make_response(rv) # 生成 response 对象
try:
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
使用 Flask 类中的 make_response()
方法生成响应对象(我们从 flask 导入并在视图函数中生成响应对象的make_response是 helpers 模块中的make_response 函数),然后调用 process_response()
方法处理响应,
process_response()
代码如下:
def process_response(self, response):
ctx = _request_ctx_stack.top
bp = ctx.request.blueprint
funcs = ctx._after_request_functions
if bp is not None and bp in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
if None in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[None]))
for handler in funcs:
response = handler(response)
if not self.session_interface.is_null_session(ctx.session):
self.session_interface.save_session(self, ctx.session, response)
return response
在把响应发送给 WSGI 服务器前执行所有使用after_request 钩子注册的函数,这里如果有蓝图的话需要增加蓝图的后处理函数。然后根据session对象来设置 cookie。