序言
上篇文章写了 Flask 与 WSGI 的相关知识,详情请看 源码阅读 – Flask v0.1 [02] Flask 与 WSGI 相关知识 ,这篇文章写 Flask 请求响应循环。
Flask 中的请求响应循环
对于 Flask 的工作流程 , 最好的了解方法是从启动程序的脚本开始 , 跟着程序调用的脚步一步步深入代码的内部 。 在本节 , 我们会了解请求 – 响应循环在 Flask 中是如何处理的 : 从程序开始运行 , 第一个请求进入 , 再到返回生成的响应 。
为了方便进行单步调试 , 在这里先创建一个简单的 Flask 程序 :
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, Flask!' # 在这一行设置断点
首先在 hello 程序的 index 视图中渲染模板这一行设置断点 , 然后 PyCharm 中运行调试 。
程序启动
目前有两种方法启动开发服务器 , 一种是在命令行中使用 flask run
命令 (会调用 flask.cli.run_command()
函数) , 另一种是使用在新版本中被弃用的 flask.Flask.run()
方法 。 不论是 run_command()
函数 , 还是新版本中用于运行程序的 run()
函数 , 它们都在最后调用了 werkzeug.serving
模块中的 run_simple()
函数 , 其代码如下 :
class Flask(object):
def run(self, host='localhost', port=5000, **options):
from werkzeug import run_simple
if 'debug' in options:
self.debug = options.pop('debug')
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
return run_simple(host, port, self, **options) # run_simple
[werkzeug/serving.py]
def run_simple(hostname, port, application, use_reloader=False,
use_debugger=False, use_evalex=True,
extra_files=None, reloader_interval=1, threaded=False,
processes=1, request_handler=None, static_files=None,
passthrough_errors=False, ssl_context=None):
if use_debugger: # 判断是否使用调试器
from werkzeug.debug import DebuggedApplication
application = DebuggedApplication(application, use_evalex)
if static_files:
from werkzeug.wsgi import SharedDataMiddleware
application = SharedDataMiddleware(application, static_files)
def inner():
make_server(hostname, port, application, threaded,
processes, request_handler,
passthrough_errors, ssl_context).serve_forever()
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
display_hostname = hostname != '*' and hostname or 'localhost'
if ':' in display_hostname:
display_hostname = '[%s]' % display_hostname
_log('info', ' * Running on %s://%s:%d/', ssl_context is None
and 'http' or 'https', display_hostname, port)
if use_reloader: # 判断是否使用重载器
# Create and destroy a socket so that any exceptions are raised before
# we spawn a separate Python interpreter and lose this ability.
test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
test_socket.bind((hostname, port))
test_socket.close()
run_with_reloader(inner, extra_files, reloader_interval)
else:
inner()
run()
函数最后一行是 return run_simple(host, port, self, **options)
, 而 run_simple()
函数的第三个参数是 application
, 实际使用的时候是 self
, 指的就是 Flask 对象本身 , 因此会调用当前对象的 __call__()
方法进行请求的处理 , 这时就会运行 wsgi_app()
。
在这里使用了两个 Werkzeug 提供的中间件 , 如果 use_debugger 为 Ture , 也就是开启调试模式 , 那么就使用 DebuggedApplication 中间件为程序添加调试功能 。 如果 static_files 为 True , 就使用 SharedDataMiddleware 中间件为程序添加提供 (serve) 静态文件的功能 。
这个方法最终会调用 inner()
函数 , 函数中的代码和之前创建的 WSGI 程序末尾很像 。 它使用 make_server() 方法创建服务器 , 然后调用 serve_forever() 方法运行服务器 。 为了避免偏离重点 , 中间在 Werkzeug 和其他模块的调用我们不再分析 。 我们在前面学习过 WSGI 的内容 , 当接收到请求时 , WSGI 服务器会调用 Web 程序中提供的可调用对象 , 这个对象就是我们的程序实例 app 。 现在 , 第一个请求进入了 。
请求 In
Flask类实现了 __call__()
方法 , 当程序实例被调用时会执行这个方法 , 而这个方法内部调用了 Flask.wsgi_app()
方法 , 如下所示 :
[flask.py]
class Flask(object):
def wsgi_app(self, environ, start_response):
with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`"""
return self.wsgi_app(environ, start_response)
通过 wsgi_app()
方法接收的参数可以看出来 , 这个 wsgi_app()
方法就是隐藏在 Flask 中的那个 WSGI 程序 。 这里将 WSGI 程序实现在单独的方法中 , 而不是直接实现在 __call__()
方法中 , 主要是为了在方便附加中间件的同时保留对程序实例的引用 。 WSGI 程序调用了 preprocess_request()
方法对请求进行预处理 (request preprocessing) , 这会执行所有使用 before_request
钩子注册的函数 。
如果预处理没有结果 , 即为空 , 然后执行 dispatch_request
, 用于请求调度 , 它会匹配并调用对应的视图函数 , 获取其返回值 , 在这里赋值给 rv
。 请求调度的具体细节我们会在后面了解 。 最后 , 接收视图函数返回值的 make_response
会使用这个值来生成响应 。 完整的调度在 wsgi_app
中已经写明了 。
响应 Out
而最终的处理也是在 wsgi_app 中 , 如下 :
def wsgi_app(self, environ, start_response):
with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
在函数的最后三行 , 使用 Flask 类中的 make_response()
方法生成响应对象 , 然后调用 process_response()
方法处理响应 。 返回作为响应的 response
后 , 代码执行流程就回到了 wsgi_app()
方法 , 最后返回响应对象 , WSGI 服务器接收这个响应对象 , 并把它转换成 HTTP 响应报文发送给客户端 。 就这样 , Flask 中的请求 – 循环之旅结束了 。
结语
本节就先到此结束了,后面接着写。
如有错误,敬请指出,感谢指正! — 2021-05-10 22:27:50
最新评论
这个软件有bug的,客户端windows有些键不能用如逗号、句号
没有收到邮件通知
我的评论通知貌似坏掉了,定位一下问题
测试一下重新部署后的邮件功能
居然看到自己公司的MIB库,诚惶诚恐
那可能是RobotFramework-ride的版本问题。我装的1.7.4.2,有这个限制。我有空再尝试下旧版本吧,感谢回复。
你好!我在python2.7中安装RobotFramework-ride的时候提示wxPython的版本最高是2.18.12,用pip下载的wxPython版本是4.10,而且我在那个路径下没有找到2
真的太好了,太感谢了,在bilibili和CSDN上都找遍了,终于在你这里找到了