flask源码笔记(2) 最简单的flask app分析
github地址:包括所有实例的代码 https://github.com/kurset/learn_flask_code
不定期更新。
下面我们尝试分析一下一个最简单的flask应用都发生了什么
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'hello world'
if __name__ == '__main__':
app.run()
大致这段程序flask要做四件事情
- 初始化Flask的对象
- 建立url和对应的视图处理函数
- 启动一个flask的开发服务器
- 解析请求url并调用相应的视图处理函数
初始化Flask的对象
在源码中,Flask类位于app.py中,是最重要的类,大约有两千多行代码,先来看看__init__方法
def __init__(self, import_name, static_path=None, static_url_path=None,
static_folder='static', template_folder='templates',
instance_path=None, instance_relative_config=False,
root_path=None):
基本上只需要传入一个模块名,其他的一些设置项都有自己的默认值。但这个方法里面初始化有些数据很重要,比如
self.view_functions = {}
self.url_map = Map()
大概也能看出来,这分别保存了Flask对象的url数据结构和视图函数。
建立url和对应的视图处理函数
Flask是如何建立url和视图处理函数呢
当然是找route的装饰器了。装饰器定义很简单
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
就是取出endpoint的设置项,并进行add_url_rule的操作。
那么endpoint是什么呢,我们可以把它看做是url_map和view_function对应的key值,通过这个值,把url和视图函数联系起来。但一般我们并没有设置这个值,在add_url_rule函数中,我们可以看到Flask调用了_endpoint_from_view_func方法,实际上endpoint取的是view_func.name。
接下来,在add_url_rule方法中可以看到
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
self.url_map.add(rule)
url_rule_class是Flask封装的一个Rule的Class
url_rule_class = Rule
新初始化一个rule的class并加入到url_map中。
启动一个flask的开发服务器
这个说法并不准确,因为当我们看Flask类中run()方法时候,可以看到Flask的开发服务器实际上是werkzeug提供的。
# run方法中 简化代码
from werkzeug.serving import run_simple
run_simple(host, port, self, **options)
解析请求url并调用相应的视图处理函数
这是一个比较麻烦的过程,先从Flask的wsgi_app看起,从wsgi中接到environ基本的请求信息,Flask首先保存请求信息并压入请求栈中,这个细节先不去探究
ctx = self.request_context(environ)
ctx.push()
之后调用full_dispatch_request方法,处理请求。
在full_dispatch_request里调用full_dispatch_request方法
def dispatch_request(self):
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule = req.url_rule
# if we provide automatic options for this URL and the
# request came with the OPTIONS method, reply automatically
if getattr(rule, 'provide_automatic_options', False) \
and req.method == 'OPTIONS':
return self.make_default_options_response()
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args)
可以看到先从当前的请求上下文中找到request信息,之后在request信息中保留url的endpoint,再从view_functions里面根据endpoint找到视图函数,并返回执行结果。
full_dispatch_request最后会调用一个finalize_request方法,将结果包装成response_class,最终返回给用户。
def finalize_request(self, rv, from_error_handler=False):
response = self.make_response(rv)
try:
response = self.process_response(response)
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception('Request finalizing failed with an '
'error while handling an error')
return response