flask在通过route装饰器添加路由时,endpoint参数默认是所装饰函数的名称。一直以来,对这个endpoint很不理解,认为它很多余,直到最近自己阅读源码,又参考别人的源码解读,总算是对这个设计的作用有了一定的理解。
一个url请求由哪个视图函数来处理,是由route装饰器决定的,通常是下面的形式
@app.route('/')
def home():
return 'welcome'
route装饰器的endpoint参数默认是所装饰函数的名称,你也可以对它进行设置
@app.route('/', endpoint='myhome')
def home():
return 'welcome'
route装饰器内部调用了add_url_rule方法,这个方法所实现的最主要的功能总结下来有两个
下面这段代码可以印证前面所说的两个功能
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
以上只是add_url_rule方法代码中的一部分,没有展示出来的部分有一行至为关键
options["endpoint"] = endpoint
在使用url_rule_class创建rule时,options作为参数,这意味着rule中包含了endpoint的信息,除此以外还有url的信息,这一点请务必牢记,接下来将解释他们之间的关系。
当一个请求到来时,flask需要根据请求的url找到与之对应的那个视图函数,并用视图函数处理请求,这部分功能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 (
getattr(rule, "provide_automatic_options", False)
and req.method == "OPTIONS"
):
return self.make_default_options_response()
return self.view_functions[rule.endpoint](**req.view_args)
在这个函数里,先获得请求对象req,通过req获得路由rule,最后根据rule里的endpoint从view_functions中找到所需要的视图函数
url 与 视图函数之间并不是一一映射的关系,换言之,一个url并不清楚自己将由哪个视图函数来处理,它只知道自己对应着一个endpoint,view_functions这个字典存储这endpoint与视图函数之间的映射关系,这样,就实现了url与视图函数之间的解耦。
接下来,用一个特定场景来解释解耦的意义。
一个网站的首页,其url一般有多种形式,比如 / 可以表示首页的url,这几乎是确定的, /index 也可以带用户来到网站首页,那么这两个url就对应着同一个响应,但这同一个响应可以是同一个视图函数来完成么?之前,我都是这样来处理的
from flask import Flask, redirect
app = Flask(__name__)
@app.route('/')
def home():
return 'welcome'
@app.route('/index')
def index():
return redirect('/')
if __name__ == '__main__':
app.run(port=5050, debug=True)
当/index请求到来时,在index函数中做重定向,这样就不必实现home函数里的内容了,但无缘无故的增加一次重定向,总感觉不舒服,难道没有别的办法来解决多个url对应同一个视图函数的问题么?当然有,用endpoint, 修改上面的代码,去掉函数index, 增加一行新代码
# @app.route('/index')
# def index():
# return redirect('/')
app.add_url_rule('/index', 'index', home)
URL /index 对应着endpoint index, index 对应着home函数,这样,当/index请求到来时,直接使用home函数来处理,岂不美哉。
QQ交流群: 211426309