Flask源码剖析二:路由内部完成道理
2019-11-18杂谈搜奇网27°c
A+ A-媒介
Flask是目前为止我最喜欢的一个Python Web框架了,为了更好的控制其内部完成机制,这两天预备进修下Flask的源码,将由浅入深跟人人分享下,个中Flask版本为1.1.1。
上次了解了Flask效劳的启动流程,本日我们来看下路由的内部完成机理。
Flask系列文章:
- Flask开辟初探
- Flask源码剖析一:效劳启动
关于路由
所谓路由,就是处置惩罚要求URL和函数之间关联的顺序。
Flask中也是对URL划定规矩举行统一治理的,建立URL划定规矩有两种体式格局:
- 运用@app.route润饰器,并传入URL划定规矩作为参数,将函数绑定到URL,这个历程便将一个函数注册为路由,这个函数则被称为视图函数。
- 运用app.add_url_rule()。
在最先浏览源码之前,我是有这几点疑问的?
- 注册路由的历程是什么?
- Flask内部是如何举行URL划定规矩治理的?
- 一个视图函数绑定多个URL内部是如何完成的?
- 动态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
route()有两个参数,rule示意url划定规矩。该函数对参数举行处置惩罚以后,挪用要领add_url_role(),这里也就考证了两种注册路由的要领等价。我们来看下代码:
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 the methods are not given and the view_func object knows its
# methods we can use that instead. If neither exists, we go with
# a tuple of only ``GET`` as default.
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 that should always be added
required_methods = set(getattr(view_func, "required_methods", ()))
# starting with Flask 0.8 the view_func object can disable and
# force-enable the automatic options handling.
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
# Add the required methods now.
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
入参包含:
- rule: url划定规矩
- endpoint : 要注册划定规矩的endpoint,默许是视图函数的名儿
- view_func: 视图函数
- provide_automatic_options: 要求要领是不是增加OPTIONS要领的一个标志
- options: 关于要求处置惩罚的一些要领等
能够看到,add_url_rule()起首举行参数处置惩罚,包含:
- endpoint默许为视图函数的name
- url要求的要领默许为GET
- 若要求要领中没有设置OPTIONS,增加该要领。
在处置惩罚完一切的参数后,将该URL划定规矩写入url_map(建立好Rule对象,并增加到Map对象中),将视图函数写入view_function字典中。
个中,url_map 是werkzeug.routing:Map
类的对象,rule是 werkzeug.routing:Rule
类的对象,也就是Flask的中心路由逻辑是在werkzeug中完成的。
werkzeug
werkzeug是运用Python编写的一个WSGI东西集,werkzeug.routing模块重要用于url剖析。
Rule类
Rule类继续自RuleFactory类,一个Rule实例代表一个URL形式,一个WSGI运用会处置惩罚很多个差别的URL形式,与此同时发生很多个Rule实例,这些实例将作为参数传给Map类。
Map类
Map类组织的实例存储一切的url划定规矩,剖析并婚配要求对应的视图函数。
路由婚配
在运用初始化的历程当中,会注册一切的路由划定规矩,能够挪用(app.url_map)检察,当效劳收到URL要求时,就需要举行路由婚配,以找到对应的视图函数,对应的流程和道理是什么呢?
当用户要求进入Flask运用时,挪用Flask类的wsgi_app要领:
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
该函数的处置惩罚历程包含:
- 建立RequestContext对象,在对象初始化的历程当中挪用app.create_url_adapter()要领,将要求参数environ传给Map对象建立MapAdapter对象,保存在url_adapter字段中
- 将RequestContext对象推入_request_ctx_stack栈中
- 经由过程RequestContext的match_request要领,挪用MapAdapter对象的match要领找到婚配的Rule并剖析出参数,保存在request的url_rule和view_args字段中
- 挪用full_dispatch_request()
接下来我们看下full_dispatch_request要领:
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
能够看到,重点实行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,找到对应的endpoint,继而从view_functions中找到对应的视图函数,通报要求参数,视图函数处置惩罚内部逻辑并返回,完成一次要求分发。
以上,就是Flask路由的内部完成道理。