Flask 源码解读
一:Flask 整体框架图
下面是我画的一个Flask整体框架图,大家可以对照着这个图来阅读下边的源码解读。
二:route源码分析
route装饰器在app.py的Flask类中,它完成了路由的注册和视图函数的注册。
阅读 route源码之前我们先看看了解flask中,构建路由规则的两种方法:
- 通过 @app.route()
- 通过 app.add_url_rule,这个方法的签名为 add_url_rule(self,
rule, endpoint=None, view_func=None, **options),参数的含义如下:
(1) rule:url 规则字符串,可以是静态的 /path,也可以包含 /
(2) endpoint:要注册规则的endpoint,默认是 view_func 的名字
(3) view_func:对应 url 的处理函数,也被称为视图函数
这两种方法是等价的也就是说:
@app.route('/')
def hello():
return ''hello,world ! ''
也可以写成
def hello():
return "helo world ! "
app.add_url_rule('/', 'hello', hello)
下面我们来看 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
通过装饰器将url传入到add_url_rule当中的rule,methods以**options关键字参数传入到add_url_rule中,这也验证了上面两种方法等价的说法。
def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=None,
**options
):
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options["endpoint"] = endpoint
methods = options.pop("methods", None)
在add_url_rule中,判断endpoint是否为空,为空调用_endpoint_from_view_func函数从视图函数中以字符串取出视图函数名,并将endpoint添加到字典options当中
def _endpoint_from_view_func(view_func):
assert view_func is not None, "expected view func if endpoint is not provided."
return view_func.__name__
_endpoint_from_view_func函数返回视图函数的__name__属性
if methods is None:
methods = getattr(view_func, "methods", None) or ("GET",) #提取属性,有过没有mnethods属性就给他一个GET
if provide_automatic_options is None:
if "OPTIONS" not in methods:
provide_automatic_options = True
required_methods.add("OPTIONS")
else:
provide_automatic_options = False
methods |= required_methods #连接字典结果放在methods中
这块是构建 methods 的部分逻辑,有几个python的知识点我给注释了
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
self.url_map.add(rule)
self.url_rule_class我们跟进去会发现他就是routing.py下的Rule类。所以此处是将rule、methods、**options传入Rule类构造出一个rule对象,将该rule对象添加到url_map当中.其中 options[‘endpoint’]=endpoint, rule=url . 通过routing.py的Rule类建立了(url,endpoint,methods)的映射关系
self.url_map = self.url_map_class()
url_map_class = Map
上边两步是我们跟url_map的过程,url_map是routing.py的Map类创建出来的一个对象,用来存储Rule的对象
url_map = Map([
Rule('/',endpoint=/index/),
Rule('static',endpoint='static'
])
如上Map内是用列表来存储Rule的对象
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError(
"View function mapping is overwriting an "
"existing endpoint function: %s" % endpoint
)
self.view_functions[endpoint] = view_func
上面这段代码主要做的事情就是更新 self.url_map 和 self.view_functions 两个变量。
系统梳理:
add_url_rule的view_func就是app.route构造器修饰的视图函数,
app.py的Flask类定义了view_functions为一个空字典,存储了所有的{endpoint:view_func}映射关系;
Flask的url_map存储了所有的(url,endpoint)映射关系;
至此,url与视图函数就通过endpoint映射起来了,请求时服务器对请求进行转发,解析得到url,通过MapAdapter类的match方法得到endpoint,再通过view_functions找到对应的视图函数,直接调用函数执行,就完成了一个完整的route
三:请求到响应源码
我们通过一个实例引入:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
就这样flask实例就生成了
但是当调用app的时候,实际上调用了Flask的__call__方法, 这就是app工作的开始
Flask的__call__源码如下:
def __call__(self