当前位置:网站首页>Flask1.1.4 Werkzeug1.0.1 源码分析:路由
Flask1.1.4 Werkzeug1.0.1 源码分析:路由
2022-07-07 18:16:00 【某工程师$】
路由大致分为两部分:
- 路由绑定:将url_rule和view_func 绑定到falsk实例中,这个在之前的一篇 启动流程中已经讲过了
- 路由解析:当实际请求来时,根据environ去匹配到对应的Rule,并从environ中解析出参数,然后根据Rule.endpoint 去view_functions中找对应的view_function进行调用
此篇文章重点讲解第二部分,Flask的路由匹配其实基于 werkzeug.routing.Map 和 werkzeug.routing.Rule
我们可以先写个简单的demo,了解下这两个工具的使用方法。
# 构造map
url_map = Map([Rule("/a", endpoint='a'), Rule("/b", endpoint='b'), Rule("/c/<path_param>", endpoint='c')])
# 调用Map.bind() 返回 MapAdapter对象
urls = url_map.bind('eee.com', '/')
# MapAdapter.match() 进行请求匹配
print(urls.match("/a"))
print(urls.match("/c/123"))
print(urls.match("/dd"))
结果如下:
#匹配到了会返回 (endpoint, view_func_args)
('a', {
})
('c', {
'path_param': '123'})
#匹配不到 抛异常
Traceback (most recent call last):
File "/Users/panc/Documents/projects/PycharmProjects/grpc-client/pycode/flask_demo.py", line 9, in <module>
print(urls.match("/dd"))
File "/usr/local/Caskroom/miniconda/base/envs/python37/lib/python3.7/site-packages/werkzeug/routing.py", line 1945, in match
raise NotFound()
werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
ok,下面开始研究Flask的路由过程~
首先要注意下,Flask中并没有直接把URL和view_func关联起来,而是在其中添加了endpoint的概念。其实也好理解,URL很多时候并不是固定的,还包含了参数部分,不太适合作为key。另外,从处理请求的步骤来看,首先要根据请求url信息去匹配已经注册了的url_rule,然后再去找对应的view_func执行逻辑。这样分为两步的情况下,将url信息和view_func分开存储,然后通过endpoint来关联两者显得很合适。
启动流程一篇中已经讲过,HTTP格式数据转换为WSGI格式数据后,调用 flask_app(environ, start_response) 执行具体的处理逻辑
class Flask(_PackageBoundObject):
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response):
# 此处构建了 flask.ctx.RequestContext 对象
# 构建过程中包含 路由处理逻辑
ctx = self.request_context(environ)
error = None
try:
try:
# 此处也包含了路由逻辑 将当前请求上下文入栈
# 入栈是为了用非传参的方式将一些信息传递给后面的view_function
ctx.push()
# RequestContext 对象构建和push的过程中已经处理好了路由的逻辑
# 之后只需要获取当前请求的 RequestContext实例,用endpoint去获取并调用方法即可
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)
def request_context(self, environ):
# 注意第一个参数为flask实例
return RequestContext(self, environ)
def create_url_adapter(self, request):
if request is not None:
subdomain = (
(self.url_map.default_subdomain or None)
if not self.subdomain_matching
else None
)
# 之前的demo里面有看过这个 bind_to_environ 和bind作用是一样的
# 返回一个 MapAdapter对象
return self.url_map.bind_to_environ(
request.environ,
server_name=self.config["SERVER_NAME"],
subdomain=subdomain,
)
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)
# 此处将view_function的返回 转换为 Response对象
return self.finalize_request(rv)
def dispatch_request(self):
# 取出当前的RequestContext对象 并获取其request属性
req = _request_ctx_stack.top.request
# 路由匹配失败 抛异常
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule = req.url_rule
# 最终 Rule对象的endpoint去找到对应的view_function并用参数调用 执行具体逻辑
return self.view_functions[rule.endpoint](**req.view_args)
下面来看下路由处理的关键 RequestContext
class RequestContext(object):
def __init__(self, app, environ, request=None, session=None):
# 此处保存了 flask app实例
self.app = app
if request is None:
# 使用environ 构建一个 Request对象
request = app.request_class(environ)
self.request = request
self.url_adapter = None
try:
# 此处 就是进行Map.bind 获得了一个 MapAdapter对象
self.url_adapter = app.create_url_adapter(self.request)
except HTTPException as e:
self.request.routing_exception = e
self.flashes = None
self.session = session
def match_request(self):
try:
# 其实就是 MapAdapter.match() 此处没有传参因为之前是 Map.bind_to_environ()
result = self.url_adapter.match(return_rule=True)
# 拆包 url_rule即Rule对象包含endpoint信息 view_args即参数
# 也就是说 RequestContext对象push()时,已经完成了路由解析过程了
# 将解析结果绑定到 RequestContext对象的request属性上
self.request.url_rule, self.request.view_args = result
except HTTPException as e:
self.request.routing_exception = e
def push(self):
"""Binds the request context to the current context."""
# 将当前的请求上下文对象入栈 方便后续传递
_request_ctx_stack.push(self)
if self.url_adapter is not None:
# 关键
self.match_request()
ok,至此路由的解析过程就结束啦~
总结一下:
- 核心的核心其实还是 werkzeug的 Map、Rule、MapAdapter对象
- 路由的绑定走的是 flask_app.add_url_rule() 方式有多种,可以是 app.route() 或者用 blueprint,但是本质都是 flask_app.add_url_rule()
- 路由的解析,本质就是 map_adapter = url_map.bind_to_environ() -> rule, view_args = map_adapter.match() -> view_functions[rule.endpoint] (**view_args)
- 其中 url_map.bind_to_environ() 在 构建RequestContext对象时执行, match操作在 RequestContext.push()时执行。上下文对象准备完成并入栈之后,只需取出当前的请求上下文对象,然后用endpoint去找到对应的view_func并执行即可。
边栏推荐
- 力扣 2315.统计星号
- Version selection of boot and cloud
- Force buckle 989 Integer addition in array form
- Kubernetes -- detailed usage of kubectl command line tool
- How C language determines whether it is a 32-bit system or a 64 bit system
- 大厂经典指针笔试题
- [résolution] le paquet « xxxx» n'est pas dans goroot
- Traversal of Oracle stored procedures
- 有用的win11小技巧
- JVM class loading mechanism
猜你喜欢

Data island is the first danger encountered by enterprises in their digital transformation

力扣 2319. 判断矩阵是否是一个 X 矩阵

力扣 599. 两个列表的最小索引总和

整型int的拼接和拆分

Open source heavy ware! Chapter 9 the open source project of ylarn causal learning of Yunji datacanvas company will be released soon!

CIS芯片测试到底怎么测?

机器学习笔记 - 使用Streamlit探索对象检测数据集

Chapter 9 Yunji datacanvas company won the highest honor of the "fifth digital finance innovation competition"!

I wrote a markdown command line gadget, hoping to improve the efficiency of sending documents by garden friends!

How to test CIS chip?
随机推荐
Data island is the first danger encountered by enterprises in their digital transformation
网络原理(1)——基础原理概述
831. KMP string
Simulate the implementation of string class
图扑数字孪生煤矿开采系统,打造采煤“硬实力”
Force buckle 912 Sort array
PHP method of obtaining image information
九度 1201 -二叉排序数遍历- 二叉排序树「建议收藏」
【解决】package ‘xxxx‘ is not in GOROOT
开发那些事儿:Go加C.free释放内存,编译报错是什么原因?
Force buckle 1037 Effective boomerang
微服务远程Debug,Nocalhost + Rainbond微服务开发第二弹
Vulnhub tre1
Force buckle 1232 Dotted line
有了ST7008, 蓝牙测试完全拿捏住了
Chapter 9 Yunji datacanvas company won the highest honor of the "fifth digital finance innovation competition"!
sql 常用优化
有用的win11小技巧
Force buckle 989 Integer addition in array form
Traversée des procédures stockées Oracle