flask 源码解析:路由
这是 flask 源码解析系列文章的其中一篇,本系列所有文章列表:
构建路由规则一个 web 应用不同的路径会有不同的处理函数,路由就是根据请求的 URL 找到对应处理函数的过程。 在执行查找之前,需要有一个规则列表,它存储了 url 和处理函数的对应关系。最容易想到的解决方案就是定义一个字典,key 是 url,value 是对应的处理函数。如果 url 都是静态的(url 路径都是实现确定的,没有变量和正则匹配),那么路由的过程就是从字典中通过 url 这个 key ,找到并返回对应的 value;如果没有找到,就报 404 错误。而对于动态路由,还需要更复杂的匹配逻辑。flask 中的路由过程是这样的吗?这篇文章就来分析分析。 在分析路由匹配过程之前,我们先来看看?
这两种方法是等价的,也就是说: @app.route('/')def?hello(): ????return?"hello,?world!" 也可以写成 def?hello(): ????return?"hello,?world!"app.add_url_rule('/',?'hello',?hello) NOTE: 其实,还有一种方法来构建路由规则——直接操作? 注册路由规则的时候,flask 内部做了哪些东西呢?我们来看看? def?route(self,?rule,?**options): ????"""A?decorator?that?is?used?to?register?a?view?function?for?a ????given?URL?rule.??This?does?the?same?thing?as?:meth:`add_url_rule` ????but?is?intended?for?decorator?usage. ????""" ????def?decorator(f): ????????endpoint?=?options.pop('endpoint',?None) ????????self.add_url_rule(rule,?endpoint,?f,?**options) ????????return?f????return?decorator
def?add_url_rule(self,?endpoint=None,?view_func=None,?**options): ????"""Connects?a?URL?rule.??Works?exactly?like?the?:meth:`route` ????decorator.??If?a?view_func?is?provided?it?will?be?registered?with?the ????endpoint. ????""" ????methods?=?options.pop('methods',?None) ????rule?=?self.url_rule_class(rule,?methods=methods,?**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 上面这段代码省略了处理 endpoint 和构建 methods 的部分逻辑,可以看到它主要做的事情就是更新? 需要注意的是:每个视图函数的 endpoint 必须是不同的,否则会报? werkzeug 路由逻辑事实上,flask 核心的路由逻辑是在? >>>?m?=?Map([...?????Rule('/',?endpoint='index'),...?????Rule('/downloads/',?endpoint='downloads/index'),...?????Rule('/downloads/<int:id>',?endpoint='downloads/show')...?])>>>?urls?=?m.bind("example.com",?"/")>>>?urls.match("/",?"GET")('index',?{})>>>?urls.match("/downloads/42")('downloads/show',?{'id':?42})>>>?urls.match("/downloads")Traceback?(most?recent?call?last): ??...RequestRedirect:?http://example.com/downloads/>>>?urls.match("/missing")Traceback?(most?recent?call?last): ??...NotFound:?404?Not?Found 上面的代码演示了? 可以发现, flask 路由实现好,有了这些基础知识,我们回头看? def?dispatch_request(self): ????"""Does?the?request?dispatching.??Matches?the?URL?and?returns?the ????return?value?of?the?view?or?error?handler.??This?does?not?have?to ????be?a?response?object.??In?order?to?convert?the?return?value?to?a ????proper?response?object,?call?:func:`make_response`. ????""" ????req?=?_request_ctx_stack.top.request????if?req.routing_exception?is?not?None: ????????self.raise_routing_exception(req) ????rule?=?req.url_rule????#?dispatch?to?the?handler?for?that?endpoint ????return?self.view_functions[rule.endpoint](**req.view_args) 这个方法做的事情就是找到请求对象? 我们可以先这样理解: class?RequestContext(object): ????def?__init__(self,?app,?environ,?request=None): ????????self.app?=?app ????????self.request?=?request ????????self.url_adapter?=?app.create_url_adapter(self.request) ????????self.match_request() ????def?match_request(self): ????????"""Can?be?overridden?by?a?subclass?to?hook?into?the?matching ????????of?the?request. ????????""" ????????try: ????????????url_rule,?self.request.view_args?=? ????????????????self.url_adapter.match(return_rule=True) ????????????self.request.url_rule?=?url_rule????????except?HTTPException?as?e: ????????????self.request.routing_exception?=?eclass?Flask(_PackageBoundObject): ????def?create_url_adapter(self,?request): ????????"""Creates?a?URL?adapter?for?the?given?request.??The?URL?adapter ????????is?created?at?a?point?where?the?request?context?is?not?yet?set?up ????????so?the?request?is?passed?explicitly. ????????""" ????????if?request?is?not?None: ????????????return?self.url_map.bind_to_environ(request.environ,????????????????server_name=self.config['SERVER_NAME']) 在初始化的时候,会调用? 整个?
虽然讲完了? def?match(self,?path): ????????"""Check?if?the?rule?matches?a?given?path.?Path?is?a?string?in?the ????????form?``"subdomain|/path(method)"``?and?is?assembled?by?the?map.??If ????????the?map?is?doing?host?matching?the?subdomain?part?will?be?the?host ????????instead. ????????If?the?rule?matches?a?dict?with?the?converted?values?is?returned,????????otherwise?the?return?value?is?`None`. ????????""" ????????if?not?self.build_only: ????????????m?=?self._regex.search(path) ????????????if?m?is?not?None: ????????????????groups?=?m.groupdict() ????????????????result?=?{} ????????????????for?name,?value?in?iteritems(groups): ????????????????????try: ????????????????????????value?=?self._converters[name].to_python(value) ????????????????????except?ValidationError: ????????????????????????return ????????????????????result[str(name)]?=?value????????????????if?self.defaults: ????????????????????result.update(self.defaults) ????????????????return?result 它的逻辑是这样的:用实现 compile 的正则表达式去匹配给出的真实路径信息,把所有的匹配组件转换成对应的值,保存在字典中(这就是传递给视图函数的参数列表)并返回。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |