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 applicationtry: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 theWSGI application. This calls :meth:`wsgi_app` which can bewrapped 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 = Nonetry:try:# 把请求上下文压栈ctx.push()# 正确的请求处理路径,会通过路由找到对应的处理函数response = self.full_dispatch_request()except Exception as e:# 错误处理,默认是 InternalServerError 错误处理函数,客户端会看到服务器 500 异常error = eresponse = self.handle_exception(e)except: # noqa: B001error = sys.exc_info()[1]raisereturn 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 requestpre and postprocessing as well as HTTP exception catching anderror 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_requesthook 函数
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:raiseself.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.topbp = ctx.request.blueprintfuncs = ctx._after_request_functionsif 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。
