Application Structure and Lifecycle

Flask 让编写 Web 应用程序变得非常容易。但是一个应用程序以及它处理的每个请求都有很多不同的部分。了解应用程序设置、服务和处理请求期间发生的事情将帮助您了解 Flask 中可能实现的功能以及如何构建应用程序。

Application Setup

创建 Flask 应用程序的第一步是创建应用程序对象。每个 Flask 应用程序都是 Flask 类的实例,它收集所有配置、扩展和视图。

from flask import Flask

app = Flask(__name__)
app.config.from_mapping(
    SECRET_KEY="dev",
)
app.config.from_prefixed_env()

@app.route("/")
def index():
    return "Hello, World!"

这被称为“应用程序设置阶段”,它是您在任何视图函数或其他处理程序之外编写的代码。它可以拆分到不同的模块和子包中,但是您希望成为应用程序一部分的所有代码都必须导入才能注册。

所有应用程序设置都必须在您开始服务应用程序和处理请求之前完成。这是因为 WSGI 服务器在多个工作进程之间分配工作,或者可以分布在多台机器上。如果一个工作进程中的配置发生更改,Flask 无法确保其他工作进程之间的一致性。

Flask 尝试通过在请求处理后调用与设置相关的方法时显示错误来帮助开发人员捕获其中一些设置顺序问题。在这种情况下,您将看到此错误

The setup method ‘route’ can no longer be called on the application. It has already handled its first request, any changes will not be applied consistently. Make sure all imports, decorators, functions, etc. needed to set up the application are done before running it.

但是,Flask 无法检测所有顺序错误的设置情况。一般来说,不要在请求期间运行的视图函数中修改 Flask 应用程序对象和 Blueprint 对象。这包括

  • 使用 @app.route@app.errorhandler@app.before_request 等添加路由、视图函数和其他请求处理程序。

  • 注册蓝图。

  • 使用 app.config 加载配置。

  • 使用 app.jinja_env 设置 Jinja 模板环境。

  • 设置会话接口,而不是默认的 itsdangerous cookie。

  • 使用 app.json 设置 JSON 提供程序,而不是默认提供程序。

  • 创建和初始化 Flask 扩展。

Serving the Application

Flask 是一个 WSGI 应用程序框架。WSGI 的另一半是 WSGI 服务器。在开发期间,Flask 通过 Werkzeug 提供了一个开发 WSGI 服务器,使用 flask run CLI 命令。当您完成开发后,请使用生产服务器来服务您的应用程序,请参阅 部署到生产环境

无论您使用什么服务器,它都将遵循 PEP 3333 WSGI 规范。WSGI 服务器将被告知如何访问您的 Flask 应用程序对象,即 WSGI 应用程序。然后它将开始监听 HTTP 请求,将请求数据转换为 WSGI environ,并使用该数据调用 WSGI 应用程序。WSGI 应用程序将返回转换为 HTTP 响应的数据。

  1. 浏览器或其他客户端发出 HTTP 请求。

  2. WSGI 服务器接收请求。

  3. WSGI 服务器将 HTTP 数据转换为 WSGI environ 字典。

  4. WSGI 服务器使用 environ 调用 WSGI 应用程序。

  5. Flask,WSGI 应用程序,执行其所有内部处理以将请求路由到视图函数,处理错误等。

  6. Flask 将视图函数返回值转换为 WSGI 响应数据,并将其传递给 WSGI 服务器。

  7. WSGI 服务器创建并发送 HTTP 响应。

  8. 客户端接收 HTTP 响应。

Middleware

上面的 WSGI 应用程序是一个以特定方式运行的可调用对象。中间件是一个包装另一个 WSGI 应用程序的 WSGI 应用程序。它与 Python 装饰器的概念类似。最外层的中间件将由服务器调用。它可以修改传递给它的数据,然后调用它包装的 WSGI 应用程序(或更进一步的中间件),依此类推。它可以获取该调用的返回值并进一步修改它。

从 WSGI 服务器的角度来看,只有一个 WSGI 应用程序,即它直接调用的那个。通常,Flask 是中间件链末端的“真实”应用程序。但即使 Flask 也可以调用更进一步的 WSGI 应用程序,尽管这是一个高级的、不常见的用例。

您会看到的与 Flask 一起使用的常见中间件是 Werkzeug 的 ProxyFix,它修改请求,使其看起来直接来自客户端,即使它在途中通过了 HTTP 代理。还有其他中间件可以处理服务静态文件、身份验证等。

How a Request is Handled

对于我们来说,上述步骤中有趣的部分是 Flask 被 WSGI 服务器(或中间件)调用时。在那个时候,它将做很多事情来处理请求并生成响应。最基本的是,它会将 URL 匹配到视图函数,调用视图函数,并将返回值传递回服务器。但是还有更多部分可以用来自定义其行为。

  1. WSGI 服务器调用 Flask 对象,Flask 对象调用 Flask.wsgi_app()

  2. 创建 RequestContext 对象。这会将 WSGI environ 字典转换为 Request 对象。它还会创建一个 AppContext 对象。

  3. 推送 应用程序上下文,这使得 current_appg 可用。

  4. 发送 appcontext_pushed 信号。

  5. 推送 请求上下文,这使得 requestsession 可用。

  6. 打开会话,使用应用程序的 session_interfaceSessionInterface 的实例)加载任何现有会话数据。

  7. URL 与在应用程序设置期间使用 route() 装饰器注册的 URL 规则进行匹配。如果没有匹配项,则错误(通常是 404、405 或重定向)将被存储以便稍后处理。

  8. 发送 request_started 信号。

  9. 调用任何 url_value_preprocessor() 装饰的函数。

  10. 调用任何 before_request() 装饰的函数。如果这些函数中的任何一个返回一个值,则该值将立即被视为响应。

  11. 如果 URL 在前面几个步骤中没有匹配到路由,则现在会引发该错误。

  12. 调用与匹配的 URL 关联的 route() 装饰的视图函数,并返回一个值用作响应。

  13. 如果到目前为止的任何步骤引发了异常,并且存在与异常类或 HTTP 错误代码匹配的 errorhandler() 装饰的函数,则调用该函数来处理错误并返回响应。

  14. 无论哪个返回了响应值(before request 函数、视图或错误处理程序),该值都会转换为 Response 对象。

  15. 调用任何 after_this_request() 装饰的函数,然后清除它们。

  16. 调用任何 after_request() 装饰的函数,这些函数可以修改响应对象。

  17. 保存会话,使用应用程序的 session_interface 持久化任何修改后的会话数据。

  18. 发送 request_finished 信号。

  19. 如果到目前为止的任何步骤引发了异常,并且该异常未被错误处理程序函数处理,则现在会处理它。HTTP 异常被视为具有其相应状态代码的响应,其他异常被转换为通用 500 响应。发送 got_request_exception 信号。

  20. 响应对象的状态、标头和正文返回到 WSGI 服务器。

  21. 调用任何 teardown_request() 装饰的函数。

  22. 发送 request_tearing_down 信号。

  23. 请求上下文被弹出,requestsession 不再可用。

  24. 调用任何 teardown_appcontext() 装饰的函数。

  25. 发送 appcontext_tearing_down 信号。

  26. 应用程序上下文被弹出,current_appg 不再可用。

  27. 发送 appcontext_popped 信号。

还有比这更多的装饰器和自定义点,但它们不是每个请求生命周期的一部分。它们更具体于您在请求期间可能使用的一些东西,例如模板、构建 URL 或处理 JSON 数据。请参阅本文档的其余部分以及 API 以进一步探索。