from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
if __name__ == "__main__":
app.run(debug=True)
对于route路由的工作原理和工作流程,进入源码进行探究。
flask的路由管理主要是依赖于werkzeug的
-
Rule类
用来构造不同的URL模式的对象,路由URL规则
-
Map类
存储所有的URL规则和一些配置参数
-
BaseConverter的子类
负责定义匹配规则
-
MapAdapter类
负责协调Rule做具体的匹配的工作
下面看具体如何实现
第一步:
点开
@app.route('/')
def index():
return 'index'
源码是
def route(self, rule, **options):
"""A decorator that is used to register a view function for a
given URL rule. This does the same thing as :meth:`add_url_rule`
but is intended for decorator usage::
@app.route('/')
def index():
return 'Hello World'
一个用于注册视图函数的装饰器,用于装饰
@app.route('/')
def index():
return 'Hello World'
"""
def decorator(f):
# 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中
本质是调用add_url_rule(rule, endpoint, f, **options)函数
第二步:
点开add_url_rule(rule, endpoint, f, **options)
@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
"""Connects a URL rule. Works exactly like the :meth:`route`
decorator. If a view_func is provided it will be registered with the
endpoint.
Basically this example::
@app.route('/')
def index():
pass
Is equivalent to the following::
def index():
pass
app.add_url_rule('/', 'index', index)
定义连接url的规则
1.
@app.route('/')
def index():
pass
2.
def index():
pass
app.add_url_rule('/', 'index', index)
"""
methods = options.pop('methods', None)
rule = self.url_rule_class(rule, methods=methods, **options)
self.url_map.add(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 verwriting an '
'existing endpoint function: %s' % endpoint)
self.view_functions[endpoint] = view_func
这个函数主要做的事情就是更新 self.url_map 和 self.view_functions 两个变量,将rule、methods、options传入Rule类构造出一个rule对象,将该rule对象添加到url_map当中.其中 options[‘endpoint’]=endpoint, rule=url . 通过routing.py的Rule类建立了(url,endpoint,methods)的映射关系
继续找,url_map是routing.py的Map类创建出来的一个对象,用来存储Rule的对象,rule 是 werkzeug.routing:Rule类的对象
Rule类中定义
url_map = Map([
Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
Rule('/all/page/<int:page>', endpoint='all_entries')
])
add_url_rule的view_func就是app.route构造器修饰的视图函数,app.py的Flask类定义了view_functions为一个空字典,将view_func存入view_functions中,建立了{endpoint:view_func}的映射关系
Flask的url_map存储了所有的(url,endpoint)映射关系;
Flask的view_functions存储了所有的{endpoint:view_func}映射关系;
至此,url与视图函数就通过endpoint映射起来了,请求时服务器对请求进行转发,解析得到url,通过MapAdapter类的match方法得到endpoint,再通过view_functions找到对应的视图函数,直接调用函数执行,就完成了一个完整的route.
def match(self, path_info=None, method=None, return_rule=False,
query_args=None):
"""The usage is simple: you just pass the match method the current
path info as well as the method (which defaults to `GET`).
Here is a small example for matching:
>>> m = Map([
... Rule('/', endpoint='index'),
... Rule('/downloads/', endpoint='downloads/index'),
... Rule('/downloads/<int:id>', endpoint='downloads/show')
... ])
"""
for rule in self.map._rules:
try:
rv = rule.match(path, method)
except RequestSlash:
raise RequestRedirect(self.make_redirect_url(
url_quote(path_info, self.map.charset,
safe='/:|+') + '/', query_args))
except RequestAliasRedirect as e:
raise RequestRedirect(self.make_alias_redirect_url(
path, rule.endpoint, e.matched_values, method, query_args))
if rv is None:
continue
if rule.methods is not None and method not in rule.methods:
have_match_for.update(rule.methods)
continue
if self.map.redirect_defaults:
redirect_url = self.get_default_redirect(rule, method, rv,
query_args)
if redirect_url is not None:
raise RequestRedirect(redirect_url)
if rule.redirect_to is not None:
if isinstance(rule.redirect_to, string_types):
def _handle_match(match):
value = rv[match.group(1)]
return rule._converters[match.group(1)].to_url(value)
redirect_url = _simple_rule_re.sub(_handle_match,
rule.redirect_to)
else:
redirect_url = rule.redirect_to(self, **rv)
raise RequestRedirect(str(url_join('%s://%s%s%s' % (
self.url_scheme or 'http',
self.subdomain and self.subdomain + '.' or '',
self.server_name,
self.script_name
), redirect_url)))
if return_rule:
return rule, rv
else:
return rule.endpoint, rv
if have_match_for:
raise MethodNotAllowed(valid_methods=list(have_match_for))
raise NotFound()
通过 url 找到处理该 url 的 endpoint
总结:
路由实现的流程:
- 调用函数add_url_rule() ,将rule、methods、options传入Rule类构造出一个rule对象,建立映射关系
- 将该rule对象添加到url_map当中,
view_functions存储了所有的{endpoint:view_func}映射关系,
此时url与视图函数就通过endpoint映射起来了
路由的本质是:通过 url 找到处理该 url 的 endpoint
- 最后,通过MapAdapter类的match方法得到endpoint,再通过view_functions找到对应的视图函数,直接调用函数执行,就完成了一个完整的route.