路由的endpoint有什么作用

flask在通过route装饰器添加路由时,endpoint参数默认是所装饰函数的名称。一直以来,对这个endpoint很不理解,认为它很多余,直到最近自己阅读源码,又参考别人的源码解读,总算是对这个设计的作用有了一定的理解。

1.路由规则如何添加

一个url请求由哪个视图函数来处理,是由route装饰器决定的,通常是下面的形式

@app.route('/')
def home():
    return 'welcome'

route装饰器的endpoint参数默认是所装饰函数的名称,你也可以对它进行设置

@app.route('/', endpoint='myhome')
def home():
    return 'welcome'

route装饰器内部调用了add_url_rule方法,这个方法所实现的最主要的功能总结下来有两个

  1. 创建一个rule, 并加入到url_map 中
  2. 在view_functions 中增加一个key-value对,endpoint做key,视图函数做value

下面这段代码可以印证前面所说的两个功能

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的信息,这一点请务必牢记,接下来将解释他们之间的关系。

2. 请求到来时,如何找到与之对应的的视图函数

当一个请求到来时,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中找到所需要的视图函数

3. 解耦设计

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

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案