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_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
中还定义了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_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 的优秀设计。