Inside Flask - flask.__init__.py 和核心组件
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。它包含了两个基本的成员,code和description,分别表示中止时的 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实现。Markup在markupsafe模块的__init__.py中,是对文本的一个封装,对字符串进行安全的转义,从而能方便地使用到 html 和 xml 中。Markup的实现过程中,使用到escape函数帮助转义。escape的实现其实很简单,就是把 html 中的预留字符转换为实体,代码如下:return Markup(text_type(s) .replace('&', '&') .replace('>', '>') .replace('<', '<') .replace("'", ''') .replace('"', '"') )就是把
& > < ' "这几个字符转换为实体。使用示例:
Markup('<h1>%s<h1>') % '<h1>Hello, world!<h1>' # 结果为 Markup('<h1><h1>Hello, world!<h1><h1>') -
Flask Request Response
这 3 个类从
flask.app中导入(使用了当前相对位置导入from .app import ...)。Flask是整个 Flask 框架的核心类,它实现了 WSGI 的应用接口,提供路由、模板解析、日志、异常管理等等核心功能。Request和Response分别是对请求和响应的数据的包装,实际的代码在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_forflashsend_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_appgrequest等这些对象从
flask.globals导入,在当前上下文环境中起作用,包括:current_app, g, request, session, _request_ctx_stack, _app_ctx_stack上下文模式是 web 框架中经常会使用到的模式,它表示当前会话、请求、应用的执行环境,是当前代码执行时所能使用到的变量、函数等等。不同的用户通常使用不同的上下文环境,通过相互隔离上下文环境提供一定的安全特性。上下文还与特定的实现技术有关,线程、协程会使用不同的实现方法,有时在不同的系统中会有需要注意的细节。
在实践中遇到的一些问题,特别是与 Flask 扩展有关的问题,很多是与上下文不对有关。
注意,
current_appgrequestsession均是LocalProxy,在运行时通过查找函数加载真正的对象。后续深入分析
flask.globals。 -
上下文处理
has_request_contexthas_app_contextafter_this_requestcopy_current_request_context这些函数和组件从
flask.ctx导入,辅助处理上下文。在
flask.ctx中还定义了ApplcaitonContext和RequestContext两个上下文类。RequestContext包含请求时的信息,在请求开始时创建,在请求结束后清理。ApplicationContext包含应用的信息,绑定到当前的线程和协程上。当RequestContext发现没有ApplicationContext时,也会自动隐式创建一个(在RequestContext.push()中)。后续深入分析。
-
Module 和 Blueprint
分别从
flask.module和flask.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_templaterender_template_string这两个函数从
flask.templating导入,完成模板的渲染工作。使用示例:
# 模板文件 templates/index.html <h1>{{ content }}</h1> # 视图渲染代码 @app.route('/') def index(): return render_template('index.html', content='Hello, world!')后续深入分析。
-
内部信号
signals_availabletemplate_renderedrequest_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 的优秀设计。
浙公网安备 33010602011771号