Inside Flask - flask.__init__.py 和核心组件

时间:2021-03-15 14:48:18

Inside Flask - flask.__init__.py 和核心组件

简单的示例

首先看看一个简单的示例。使用 Flask ,通常是从 flask 模块导入 Flask 、 request 等等组件。一个简单的示例如下:

from flask import Flask
app = Flask(__name__)
app.config.update(DEBUG=True) @app.route('/')
@app.route('/index')
def index():
return '<h1>Hello, world!</h1>' if __name__ == '__main__':
app.run()

这个简单示例只使用到 Flask 中的核心 Flask 对象,以及 Flask 的 route 机制,就能简单地提供一个显示 Hello, world! 字符串的 index 页面。其它更加复杂的结构和使用模式,都会基于类似的简单例子进行扩展,以满足一些特定场景的要求。

flask.__init__.py

除了 Flask 类,在 flask 模块里面还包含了其它 Flask 里的核心组件,打开这个源文件,可看到它里面包括:

  • abort

    werkzeug.exceptions 导入,调用时直接抛出异常。它的本质是 werkzeug.exceptions 里面的 Aborter() 对象。

    Aborter 使用工厂模式。其内部使用一个 dict 类型的 mapping 成员变量来保存从 http 状态码到 werkzeug.exceptions 中定义的所有异常类之间的一个映射,然后通过一个 magic 方法 __call__ 创建并抛出异常。

    所有的异常的基类为 HTTPException 。它包含了两个基本的成员,codedescription,分别表示中止时的 http 状态码和要返回给用户的信息。

    异常类定义完后,通过一个函数 _find_exceptions 把所有在这里定义的异常类,放入到 default_exceptions 这个 dict 全局变量中。Abort 的初始化函数默认用 default_exceptions 作为 mapping 的值。

    abort 支持的状态码包括 400 / 401 / 403 / 404 / 405 / 406 / 408 / 409 / 410 / 411 / 412 / 413 / 414 / 415 / 416 / 417 / 418 / 422 / 428 / 429 / 429 / 431 / 500 / 501 / 502 / 503 / 504/ 505 。

    使用示例:

      @app.route('/_404')
    def _404()
    abort(404, 'Oops, 404.')
  • redirect

    werkzeug.utils 导入的重定向函数,通常与 url_for 结合使用。

    redirect 生成一个 Response 对象,默认以 302 状态码返回响应。其响应的模板内容为(其中 escape 为 URL 转义函数):

      '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
    '<title>Redirecting...</title>\n'
    '<h1>Redirecting...</h1>\n'
    '<p>You should be redirected automatically to target URL: '
    '<a href="%s">%s</a>. If not click the link.' %
    (escape(location), display_location), code, mimetype='text/html'

    可见,当浏览器不支持 302 状态码自动重定向时,可通过点击响应的 html 中的链接进行跳转。

    使用示例:

      @app.route('/_redirect')
    def _redirect()
    return redirect(url_for('.index'))
  • Markup 和 escape

    这两个都是从 Jinja2 中导入,实际由另外一个模块 markupsafe 实现。 Markupmarkupsafe模块的 __init__.py 中,是对文本的一个封装,对字符串进行安全的转义,从而能方便地使用到 html 和 xml 中。 Markup 的实现过程中,使用到 escape 函数帮助转义。

    escape 的实现其实很简单,就是把 html 中的预留字符转换为实体,代码如下:

      return Markup(text_type(s)
    .replace('&', '&amp;')
    .replace('>', '&gt;')
    .replace('<', '&lt;')
    .replace("'", ''')
    .replace('"', '"')
    )

    就是把 & > < ' " 这几个字符转换为实体。

    使用示例:

      Markup('<h1>%s<h1>') % '<h1>Hello, world!<h1>'
    # 结果为 Markup('<h1>&lt;h1&gt;Hello, world!&lt;h1&gt;<h1>')
  • Flask Request Response

    这 3 个类从 flask.app 中导入(使用了当前相对位置导入 from .app import ...)。

    Flask 是整个 Flask 框架的核心类,它实现了 WSGI 的应用接口,提供路由、模板解析、日志、异常管理等等核心功能。

    RequestResponse 分别是对请求和响应的数据的包装,实际的代码在 flask.wrappers 中。它们继承 werkzeug.wrappers 里面的请求和响应类,根据 Flask 的设计概念的需要进行了扩展。

    后续深入分析这 3 个类

    使用示例:

      from flask import Flask, Response, request
    
      app = Flask(__name__)
    
      @app.route('/')
    def index():
    method = request.method
    return Response('Request method: %s' % method) if __name__ == '__main__':
    app.run()
  • 辅助函数 url_for flash send_file, send_from_directory

    这堆辅助函数从 flask.helpers 中导入,包括:

      url_for, flash, send_file, send_from_directory,
    get_flashed_messages, get_template_attribute, make_response, safe_join,
    stream_with_context

    后续深入分析这些函数

  • 上下文全局对象代理 current_app g request

    这些对象从 flask.globals 导入,在当前上下文环境中起作用,包括:

      current_app, g, request, session, _request_ctx_stack,
    _app_ctx_stack

    上下文模式是 web 框架中经常会使用到的模式,它表示当前会话、请求、应用的执行环境,是当前代码执行时所能使用到的变量、函数等等。不同的用户通常使用不同的上下文环境,通过相互隔离上下文环境提供一定的安全特性。上下文还与特定的实现技术有关,线程、协程会使用不同的实现方法,有时在不同的系统中会有需要注意的细节。

    在实践中遇到的一些问题,特别是与 Flask 扩展有关的问题,很多是与上下文不对有关。

    注意,current_app g request session 均是 LocalProxy,在运行时通过查找函数加载真正的对象。

    后续深入分析 flask.globals

  • 上下文处理 has_request_context has_app_context after_this_request copy_current_request_context

    这些函数和组件从 flask.ctx 导入,辅助处理上下文。

    flask.ctx 中还定义了 ApplcaitonContextRequestContext 两个上下文类。

    RequestContext 包含请求时的信息,在请求开始时创建,在请求结束后清理。

    ApplicationContext 包含应用的信息,绑定到当前的线程和协程上。当 RequestContext 发现没有 ApplicationContext 时,也会自动隐式创建一个(在 RequestContext.push() 中)。

    后续深入分析。

  • Module 和 Blueprint

    分别从 flask.moduleflask.blueprints 导入,是 Flask 的扩展机制。根据 Module 中的注释,这种方式已经过时,被 Blueprint 取代。

      .. versionchanged:: 0.7
    Modules were deprecated in favor for blueprints.

    Blueprint 是 Flask 中的主要模块化扩展方法。在编写大规模的应用时,可划分功能为多个 Blueprint,每个 Blueprint 完成特定任务。

    使用示例:

      task_bp = Blueprint('task')
    app.register_blueprint(task_bp, url_prefix='/task')

    后续深入分析。

  • 视图渲染 render_template render_template_string

    这两个函数从 flask.templating 导入,完成模板的渲染工作。

    使用示例:

      # 模板文件 templates/index.html
    <h1>{{ content }}</h1> # 视图渲染代码
    @app.route('/')
    def index():
    return render_template('index.html', content='Hello, world!')

    后续深入分析。

  • 内部信号 signals_available template_rendered request_started 等等

    Flask 提供信号机制,当某些操作发生时,通过信号方式通知其它代码,方便在这些操作发生时进行进一步处理。

    现有的信号包括:

      signals_available, template_rendered, request_started,
    request_finished, got_request_exception, request_tearing_down,
    appcontext_tearing_down, appcontext_pushed,
    appcontext_popped, message_flashed

    信号机制使用 blinker ,如果要自定义信号,需要额外安装这个模块。

  • json 支持 jsonify

    通过 json 方式进行响应。

    使用示例:

      return jsonify([1,2,3,4])
  • Session

    Session 对象,实际类型为 'flask.sessions.SecureCookieSession',需要结合 SecureCookieSessionInterface 一起使用。

    它可以用来定制 Flask 的 Session 保存方式,可放入到数据库或者 redis 里面,实现跨服务器的 session 共享。session 共享后,可通过服务器集群提升整个应用的访问处理能力。

    官方文档有一个使用 redis 实现的 server-side session http://flask.pocoo.org/snippets/75/

    后续深入分析。

总结

以上已列出 Flask 里面的关键组件和概念,如果掌握这部分的概念,则使用 Flask 的过程中所遇到的问题基本能自己动手调试和解决。刚接触像上下文、信号机制、server-side session 等概念时会存在疑惑,这些概念需要理解源代码后,实际编程使用的过程中会慢慢熟悉。

后续的文章围绕上述概念展开,并探索 Flask 如何设计和实现这些概念,在整个实现过程中学习 Flask 的优秀设计。