1. 在flask中配置URL和视图函数的路由时,首先须要在main.py中实例化一个app对象:python
1 from flask import Flask, render_template 2 3 app = Flask(__name__)
2. 而后经过app实例的route方法装饰视图函数,实现路由的配置:express
1 @app.route('/') 2 def hello_world(): 3 return 'Hellow World!'
3. 全部这里须要关注在Flask类里定义的route方法,以理解Flask内部的路由配置逻辑flask
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
可见app实例的route其实是一个带参数的装饰器,其中rule是URL规则(字符串形式),而options能够接收其余按关键字传参的配置项,在上面Hello World的例子中,options应该是一个空字典。app
这个装饰器的做用是把URL规则和视图函数交由app实例的add_url_rule方法处理,并返回被装饰函数自己,因此在main.py中视图函数名依然原来的视图函数对象的引用。dom
4. 下一部须要关注的是add_url_rule方法的内部实现:ide
在add_url_rule方法里首先处理endpoint,这里endpoint能够理解为和URL规则映射的视图函数对象函数
1 if endpoint is None: 2 endpoint = _endpoint_from_view_func(view_func) 3 options['endpoint'] = endpoint
因为在Hellow World例子里,endpoint是None, 这里会调用_endpoint_from_view_func方法:ui
1 def _endpoint_from_view_func(view_func): 2 assert view_func is not None, 'expected view func if endpoint ' \ 3 'is not provided.' 4 return view_func.__name__ # 返回被装饰视图函数的函数名
这里view_func就是被装饰的视图函数,因此endpoint就被设置成立被装饰视图函数的函数名url
因而可知,若是用户但愿endpoint不是被装饰视图函数时,须要在@app.route()里以endpoint关键子传参给定一个函数对象名spa
处理完以后,endpoint被添加到options字典中
接着add_url_rule方法继续处理methods, 这里methods能够理解为这条URL和视图的映射适用于那种Http请求方法:
1 methods = options.pop('methods', None) 2 if methods is None: 3 methods = getattr(view_func, 'methods', None) or ('GET',) 4 if isinstance(methods, string_types): 5 raise TypeError('Allowed methods have to be iterables of strings, ' 6 'for example: @app.route(..., methods=["POST"])') 7 methods = set(item.upper() for item in methods) # 最后methods是一个包含用户传入的Http请求方法,或默认GET请求方法的集合
首先从options字典里取出'methods'对应的值,在Hellow World的例子中,此时methods = None
接着,把methods设置为视图函数的‘methods’属性。
p.s.:看到这里使用了getattr函数,咱们能够发现,app.route装饰的视图,并不要求是必定要定义成函数的形式,也能够定义成一个python模块导入到main.py中,这样以来flask的视图系统就具备了更加灵活的扩展性。因此methods参数既能够做为app.route的关键字参数,也经过定义视图的模块中methods标量来定义。
若是app.route()没有传入methods参数,也没有再视图模块中定义methods变量,methods默认赋值为('GET'),可见flask中路由配置默认是对应HTTP GET请求的。
Flask要求用户传入各个的methods方法必须是字符串形式,而且放在符合python协议的可迭代对象中,不然,会抛出异常提示,上面4 - 6行代码都是在作这一层判断
最后,methods变量里的元素被取出并放入集合。
至此用户定义的URL规则和Http请求方法处理完毕。
5. 若是视图模块中有定义了'requeire_methods'参数,也须要处理:
1 required_methods = set(getattr(view_func, 'required_methods', ()))
required_methods的做用这里暂时先不关注,后续再介绍
6 接下来以前的处理的methods和required_methos进行并集处理,都添加到methods参数中
methods |= required_methods
7. 把处理好的URL规则和methods参数,以及options字典委托给app实例的url_rule_class方法作进一步的处理
1 rule = self.url_rule_class(rule, methods=methods, **options)
url_rule_class其实是一个叫Rule的类,这一步若是处理经过,参数rule会接收一个Rule的实例
8. Rule这个类的__init__方法以下:
1 class Rule(RuleFactory): 2 def __init__(self, string, defaults=None, subdomain=None, methods=None, 3 build_only=False, endpoint=None, strict_slashes=None, 4 redirect_to=None, alias=False, host=None): 5 if not string.startswith('/'): 6 raise ValueError('urls must start with a leading slash') 7 self.rule = string 8 self.is_leaf = not string.endswith('/') 9 10 self.map = None 11 self.strict_slashes = strict_slashes 12 self.subdomain = subdomain 13 self.host = host 14 self.defaults = defaults 15 self.build_only = build_only 16 self.alias = alias 17 if methods is None: 18 self.methods = None 19 else: 20 if isinstance(methods, str): 21 raise TypeError('param `methods` should be `Iterable[str]`, not `str`') 22 self.methods = set([x.upper() for x in methods]) 23 if 'HEAD' not in self.methods and 'GET' in self.methods: 24 self.methods.add('HEAD') 25 self.endpoint = endpoint 26 self.redirect_to = redirect_to 27 28 if defaults: 29 self.arguments = set(map(str, defaults)) 30 else: 31 self.arguments = set() 32 self._trace = self._converters = self._regex = self._argument_weights = None
这里再回顾一下上面给__init__方法的传入的参数:
1 rule = self.url_rule_class(rule, methods=methods, **options)
URL规则是第一个位置参数,methods以及options字典里的键值对,都被__init__方法按关键字接收
首先,若是app.route传入的URL不是一个以'/'开头的字符串,会抛出异常
self.is_leaf记录URL是否没有以‘/’结尾
而后,若是methos里有"GET"方法,而没有"HEAD",会把'HEAD'添加进入,'HEAD'的做用会把后续笔记中分析。
这里注意到,__init__里有一个self.redirect_to = redirect_to,多是能够直接在app.route()里设置视图的跳转,这个放到后面再具体分析。
能够发现,flask里把路由相关的:URL,host,适用的HTTP请求方法,endpoint视图都保存到了Rule这个类的实例中。
9. 获得Rule的实例后,回到add_url_rule方法,继续看对rule实例的处理:
1 self.url_map.add(rule)
这里url_map是Map类的一个实例,是在app实例化的时候绑定到app实例的,下面只须要关注Map类的add方法:
class Map:
... ...无关代码省略
def add(self, rulefactory): """Add a new rule or factory to the map and bind it. Requires that the rule is not bound to another map. :param rulefactory: a :class:`Rule` or :class:`RuleFactory` """ for rule in rulefactory.get_rules(self): rule.bind(self) self._rules.append(rule) self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule) self._remap = True
能够看到这里rulefactory能够接收Rule实例或者RuleFactory实例,RuleFactory实例对应另外一种设置路由的方法。在咱们这个例子里,rulefactory应该是一个Rule的实例
因此还须要进一步关注,Rule的实例的get_rules方法:
1 def get_rules(self, map): 2 yield self
get_rules方法接收两个参数,Rule的实例,和Map的实例,咱们的例子里,Map实例没有做用,这个方法直接yield返回了Rule实例
下面继续看rule实例的bind方法:
1 Class Rule: 2 ... ... 省略无关代码 3 1 def bind(self, map, rebind=False): 4 2 """Bind the url to a map and create a regular expression based on 5 3 the information from the rule itself and the defaults from the map. 6 4 7 5 :internal: 8 6 """ 9 7 if self.map is not None and not rebind: 10 8 raise RuntimeError('url rule %r already bound to map %r' % 11 9 (self, self.map)) 12 10 self.map = map 13 11 if self.strict_slashes is None: 14 12 self.strict_slashes = map.strict_slashes 15 13 if self.subdomain is None: 16 14 self.subdomain = map.default_subdomain 17 15 self.compile()
这个实在判断rule实例的map属性是否为None,若是是None,就把map实例绑定到rule实例的map实行,不然报错,这里就控制了一个rule实例只能跟一个map实例进行绑定。
以后会把rule实例append到这个map实例的self._rules列表中
以后这个map实例的_rules_by_endpoint属性的会添加这样一个键值对:rule.endpoint: [rule] 也就是 视图对象:[rule实例]
至此,整个经过app,route装饰视图,来绑定URL和视图映射关系的逻辑流程已经结束,此时
app实例的self.map保存的Map类实例里保存了一个:视图对象 和 rule实例映射的键值对。
总结起来:
--- app.route()装饰器
获取URL, 视图对象,其余opeions方法,并调用app实例的add_url_rule方法
--- add_url_rule方法:
1. 获取app.route的methods关键字参数,视图模块里定义的methods参数等Http 请求方法
这里视图能够是一个函数,也能够是一个python模块
2. 把URL,视图对象,Http请求方法,绑定到一个Rule实例(app实例的),经过app实例的url_rule_class方法。
Rule的__init__方法的其余参数来自app.route的关键字传参,能够控制一些URL的匹配规则
build_only参数可让URL不绑定任何视图,实现static文件夹等。
---- url_map.add
把Rule实例和app实例保存的map实例绑定。