(点击
上方蓝字
,快速关注我们)
来源:fanchunke1991
fanchunke.me/Flask/Flask应用中的URL处理/
如有好文章投稿,请点击 → 这里了解详情
在文章:《
一个Flask应用运行过程剖析中
》,在一个上下文环境中可以处理请求。如果不考虑在处理请求前后做的一些操作,Flask源码中真正处理请求的是dispatch_request()方法。其源码如下:
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`.
"""
try
:
endpoint
,
values
=
self
.
match_request
()
return
self
.
view_functions
[
endpoint
](
**
values
)
except
HTTPException
,
e
:
handler
=
self
.
error_handlers
.
get
(
e
.
code
)
if
handler
is
None
:
return
e
return
handler
(
e
)
except
Exception
,
e
:
handler
=
self
.
error_handlers
.
get
(
500
)
if
self
.
debug
or
handler
is
None
:
raise
return
handler
(
e
)
从上面的源码中可以看到,dispatch_request()方法做了如下的工作:
-
对请求的URL进行匹配;
-
如果URL可以匹配,则返回相对应视图函数的结果;
-
如果不可以匹配,则进行错误处理。
对于错误的处理,本文暂不做介绍。本文主要对Flask应用的URL模式以及请求处理过程中的URL匹配进行剖析。
Flask应用的url_map
Flask应用实例化的时候,会为应用增添一个url_map属性。这个属性是一个Map类,这个类在werkzeug.routing模块中定义,其主要的功能是为了给应用增加一些URL规则,这些URL规则形成一个Map实例的过程中会生成对应的正则表达式,可以进行URL匹配。相关的概念和内容可以参考:《
Werkzeug库——routing模块
》。
在Flask源码中,它通过两个方法可以很方便地定制应用的URL。这两个方法是:route装饰器和add_url_rule方法。
1. add_url_rule
def
add_url_rule
(
self
,
rule
,
endpoint
,
**
options
)
:
options
[
'endpoint'
]
=
endpoint
options
.
setdefault
(
'methods'
,
(
'GET'
,))
self
.
url_map
.
add
(
Rule
(
rule
,
**
options
))
add_url_rule方法很简单,只要向其传递一条URL规则rule和一个endpoint即可。endpoint一般为和这条URL相关的视图函数的名字,这样处理就可以将URL和视图函数关联起来。除此之外,还可以传递一些关键字参数。调用该方法后,会调用Map实例的add方法,它会将URL规则添加进Map实例中。
2. route装饰器
为了更加方便、优雅地写应用的URL,Flask实现了一个route装饰器。
def
route
(
self
,
rule
,
**
options
)
:
def
decorator
(
f
)
:
self
.
add_url_rule
(
rule
,
f
.
__name__
,
**
options
)
self
.
view_functions
[
f
.
__name__
]
=
f
return
f
return
decorator
route装饰器会装饰一个视图函数。经route装饰的视图函数首先会调用add_url_rule方法,将装饰器中的URL规则添加进Map实例中,视图函数的名字会作为endpoint进行传递。然后在该应用的view_functions中增加endpoint和视图函数的对应关系。这种对应关系可以在请求成功时方便地调用对应的视图函数。
3. 一个简单的例子
我们用一个简单的例子来说明以上过程的实现:
>>>
from
flask
import
Flask
>>>
app
=
Flask
(
__name__
)
>>>
@
app
.
route
(
'/'
)
def
index
()
:
return
"Hello, World!"
>>>
@
app
.
route
(
'/
'
)
def
user
(
username
)
:
return
"Hello, %s"
%
username
>>>
@
app
.
route
(
'/page/
'
)
def
page
(
id
)
:
return
"This is page %d"
%
id
以上代码,我们创建了一个Flask应用app,并且通过route装饰器的形式为app增加了3条URL规则。
首先
: 我们看一下Flask应用的url_map长啥样:
>>>
url_map
=
app
.
url_map
>>>
url_map
Map
([
<
Rule
'/'
(
HEAD
,
GET
)
->
index
>
,
<
Rule
'/static/
'
->
static
>
,
<
Rule
'/page/
'
(
HEAD
,
GET
)
->
page
>
,
<
Rule
'/
'
(
HEAD
,
GET
)
->
user
>
])
可以看到,url_map是一个Map实例,这个实例中包含4个Rule实例,分别对应4条URL规则,其中/static/
在Flask应用实例化时会自动添加,其余3条是用户创建的。整个Map类便构成了Flask应用app的URL“地图”,可以用作URL匹配的依据。
接下来
: 我们看一下url_map中的一个属性:_rules_by_endpoint:
>>>
rules_by_endpoint
=
url_map
.
_rules_by_endpoint
>>>
rules_by_endpoint
{
'index'
:
[
<
Rule
'/'
(
HEAD
,
GET
)
->
index
>
],
'page'
:
[
<
Rule
'/page/
'
(
HEAD
,
GET
)
->
page
>
],
'static'
:
[
<
Rule
'/static/
'
->
static
>
],
'user'
:
[
<
Rule
'/
'
(
HEAD
,
GET
)
->
user
>
]
}
可以看出,_rules_by_endpoint属性是一个字典,反映了endpoint和URL规则的对应关系。由于用route装饰器创建URL规则时,会将视图函数的名字作为endpoint进行传递,所以以上字典的内容也反映了视图函数和URL规则的对应关系。
再接下来
: 我们看一下Flask应用的view_functions:
>>>
view_functions
=
app
.
view_functions
>>>
view_functions
{
'index'
: <
function __main__
.
index
>
,
'page'
: <
function __main__
.
page
>
,
'user'
: <
function __main__
.
user
>
}
在用route装饰器创建URL规则时,它还会做一件事情:self.view_functions[f.__name__] = f。这样做是将函数名和视图函数的对应关系放在Flask应用的view_functions。由于Map实例中存储了函数名和URL规则的对应关系,这样只要在匹配URL规则时,如果匹配成功,只要返回一个函数名,那么便可以在view_functions中运行对应的视图函数。
最后
: 我们看一下URL如何和Map实例中的URL规则进行匹配。我们以/page/
这条规则为例:
>>>
rule
=
url_map
.
_rules
[
2
]
>>>
rule
<
Rule
'/page/
'
(
HEAD
,
GET
)
->
page
>
>>>
rule
.
_regex
re
.
compile
(
ur
'^\|\/page\/(?P
\d+)$'
,
re
.
UNICODE
)
>>>
rule
.
_regex
.
pattern
u
'^\\|\\/page\\/(?P
\\d+)$'
可以看到,在将一条URL规则的实例Rule添加进Map实例的时候,会为这个Rule生成一个正则表达式的属性_regex。这样当这个Flask应用处理请求时,实际上会将请求中的url和Flask应用中每一条URL规则的正则表达式进行匹配。如果匹配成功,则会返回endpoint和一些参数,返回的endpoint可以用来在view_functions找到对应的视图函数,返回的参数可以传递给视图函数。具体的过程就是:
try
:
# match_request()可以进行URL匹配
endpoint
,
values
=
self
.
match_request
()
return
self
.
view_functions
[
endpoint
](
**
values
)
...
看完本文有收获?请转
发分享给更多人
关注「P
ython开发者」,提升Python技能