由于平时在公司写后端代码都是基于Python的Flask框架,做为一个全栈(沾)工程师,我以为有必要深刻理解下框架的一些简单的原理。 首先咱们就从路由提及,注册路由是咱们平时工做最常写的东西,若是项目是MVC模式的话,咱们更是基本天天都要注册路由来完成需求。可是,最经常使用的东西每每是最容易被忽略的,咱们觉得本身天天都在写,加上Python的装饰器这种魔法,咱们想固然的认为本身对Flask框架的路由很了解。其实底层的源码不多有人看过,也没人真正想去了解其中的原理,可能这就是码农思惟致使的一些弊病:只要能完成需求就行。python
让咱们先来看看常规的注册路由的方法:后端
@app.route('/index/', methods=('GET', 'POST'))
def index():
pass
复制代码
咱们先不说让无数人高潮的装饰器,确实,这个语法糖让Python代码变得很优雅。咱们这里不讨论装饰器的利弊。可是,一样是注册路由,Flask还有一种方法也能够注册路由:app
app.add_url_rule('/index', view_func=index)
复制代码
很明显这样的代码你们也能看出来,/index就是路由的名字,view_func变量就是视图函数,可是这样不免让人浮想联翩,由于咱们知道装饰器的魔法,因此很容易让人把这两个注册路由的方法联系在一块儿。因而跟全部同样,我跳进了装饰器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
复制代码
看到这里不少人都懂了,原来装饰器实际调用的也是add_url_rule方法,只不过装饰器帮你作了这些而已,add_url_rule中rule函数就是咱们传进去的路由名字(也叫路由规则),view_func函数就是视图函数,至于methods这些都是被包括在**options里面。源码中的注释也是写的很清楚,还给力例子让人理解,看来做者也是很暖心。既然做者那么暖男,那咱们确定不假思索地跳进去看add_url_rule函数的源码:ide
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)
if methods is None:
methods = getattr(view_func, 'methods', None) or ('GET',)
if isinstance(methods, string_types):
raise TypeError('Allowed methods have to be iterables of strings, '
'for example: @app.route(..., methods=["POST"])')
methods = set(item.upper() for item in methods)
required_methods = set(getattr(view_func, 'required_methods', ()))
if provide_automatic_options is None:
provide_automatic_options = getattr(view_func,
'provide_automatic_options', None)
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
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_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 overwriting an '
'existing endpoint function: %s' % endpoint)
self.view_functions[endpoint] = view_func
复制代码
其实彻底能够去看源码的注释,简直不要太全面,里面详细写了各类规则以及结合例子来讲明这个函数的各个参数。咱们挑重要和经常使用的几个来讲:函数
先让咱们看看函数一开始的代码:post
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options['endpoint'] = endpoint
复制代码
很明显,若是咱们没有传入endpoint参数的话,endpoint就是view_func的值,也就是视图函数的名字,而后在options字典中添加endpoint的值。ui
methods = options.pop('methods', None)
if methods is None:
methods = getattr(view_func, 'methods', None) or ('GET',)
if isinstance(methods, string_types):
raise TypeError('Allowed methods have to be iterables of strings, '
'for example: @app.route(..., methods=["POST"])')
methods = set(item.upper() for item in methods)
复制代码
从注释中咱们能够了解到methods的机制是很简单的,若是代码是咱们一开始写的@app.route('/index', methods=['GET', 'POST']),那么路由能够接收get请求和post请求,没有传入methods的话methods = None。而后假如methods == None, 同时,view_func 没有methods属性的话,那么methods默认设置为('GET', ). 固然,methods不能设置为字符串类型,‘POST’能够不区分大小写。感受源码就是在教你同样。。。。url
:param rule: the URL rule as string
复制代码
注释里面有一句这样的话,意思就是传进来的路由名字是要字符串。spa
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
self.url_map.add(rule)
复制代码
路由实际上是至关复杂的,从上面的代码中能够看出来,self(Flask核心对象,就是app自己)实际上是有一个url_rule_class属性,这个属性是一个Rule类的实例,这段代码中把路由规则和methods以及其余参数都装载到这个实例中,而后再放到url_mpa里面。这里有点绕,建议有兴趣的能够本身跳进去看源码理解。
路由的简单的原理大概就是这些, 若是须要深刻了解Flask的路由的话还须要看更多的源码以及去理解才行。