1. 添加路由
1.1 添加路由源码分析
添加路由方式如下:
@app.route('/index',methods=['GET','POST'])
def index():
return 'index页面'
我们深入看route方法源码如下:
class Flask(_PackageBoundObject):
...
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::
@app.route('/')
def index():
return 'Hello World'
For more information refer to :ref:`url-route-registrations`.
:param rule: the URL rule as string
:param endpoint: the endpoint for the registered URL rule. Flask
itself assumes the name of the view function as
endpoint
:param options: the options to be forwarded to the underlying
:class:`~werkzeug.routing.Rule` object. A change
to Werkzeug is handling of method options. methods
is a list of methods this rule should be limited
to (``GET``, ``POST`` etc.). By default a rule
just listens for ``GET`` (and implicitly ``HEAD``).
Starting with Flask 0.6, ``OPTIONS`` is implicitly
added and handled by the standard request handling.
"""
def decorator(f):
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
route函数即一个闭包函数,最终返回decorator函数
add_url_rule方法解析
@setupmethod
def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=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.
Basically this example::
@app.route('/')
def index():
pass
Is equivalent to the following::
def index():
pass
app.add_url_rule('/', 'index', index)
If the view_func is not provided you will need to connect the endpoint
to a view function like so::
app.view_functions['index'] = index
Internally :meth:`route` invokes :meth:`add_url_rule` so if you want
to customize the behavior via subclassing you only need to change
this method.
For more information refer to :ref:`url-route-registrations`.
.. versionchanged:: 0.2
`view_func` parameter added.
.. versionchanged:: 0.6
``OPTIONS`` is added automatically as method.
:param rule: the URL rule as string
:param endpoint: the endpoint for the registered URL rule. Flask
itself assumes the name of the view function as
endpoint
:param view_func: the function to call when serving a request to the
provided endpoint
:param provide_automatic_options: controls whether the ``OPTIONS``
method should be added automatically. This can also be controlled
by setting the ``view_func.provide_automatic_options = False``
before adding the rule.
:param options: the options to be forwarded to the underlying
:class:`~werkzeug.routing.Rule` object. A change
to Werkzeug is handling of method options. methods
is a list of methods this rule should be limited
to (``GET``, ``POST`` etc.). By default a rule
just listens for ``GET`` (and implicitly ``HEAD``).
Starting with Flask 0.6, ``OPTIONS`` is implicitly
added and handled by the standard request handling.
"""
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options["endpoint"] = endpoint
methods = options.pop("methods", None)
# if the methods are not given and the view_func object knows its
# methods we can use that instead. If neither exists, we go with
# a tuple of only ``GET`` as default.
if methods is None:
methods = getattr(view_func, "methods", None) or ("GET",)
if isinstance(methods, string_types):
raise TypeError(
"Allowed methods have to be iterables of strings, "
'for example: @app.route(..., methods=["POST"])'
)
methods = set(item.upper() for item in methods)
# Methods that should always be added
required_methods = set(getattr(view_func, "required_methods", ()))
# starting with Flask 0.8 the view_func object can disable and
# force-enable the automatic options handling.
if provide_automatic_options is None:
provide_automatic_options = getattr(
view_func, "provide_automatic_options", None
)
if provide_automatic_options is None:
if "OPTIONS" not in methods:
provide_automatic_options = True
required_methods.add("OPTIONS")
else:
provide_automatic_options = False
# Add the required methods now.
methods |= required_methods
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
route和app_url_rule方法参数介绍
rule, 即URL规则,如:'/index'
view_func, 视图函数名称
methods=None, 即该URL支持的请求方式,如:methods=['GET','POST'];
endpoint=None, 即指定该URL的别名,如:endpoint='n1',用于反向生成URL
defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
strict_slashes=None, 对URL最后的 / 符号是否严格要求
redirect_to=None, 重定向到指定地址
subdomain=None, 子域名访问
分析decorator函数
将视图函数(如index函数),作为参数传入decorator函数,然后执行add_url_rule方法,将rule/路径信息、f/视图函数、**optins/访问方法 加入到路由的规则当中
也就是说,本质上是通过add_url_rule方法将url路径信息和视图函数添加到了路由信息表中的
那么我们从上可知,可以直接通过add_url_rule方法实现路由映射,如下:
def login():
return 'login页面'
# 路由添加方式二:
app.add_url_rule('/login',view_func=login)
1.2 添加路由的两种方式
如下
from flask import Flask,render_template,redirect
app = Flask(__name__)
'''
1. 执行decorator = app.route('/index',methords=['GET','POST'])
2. @decorator
- 执行decorator(index)
'''
# 路由添加方式一:
@app.route('/index',methods=['GET','POST'])
def index():
return 'index页面'
def login():
return 'login页面'
# 路由添加方式二:
app.add_url_rule('/login',view_func=login)
if __name__ == '__main__':
app.run()
注意:
通常在Flask中我们只用第一种方式(也就是装饰器的方法)来添加路由,第二种只是用来方便理解路由添加原理的方式,实战中并不会用到
2. 反向生成URL
通过url_for方法反向生成URL
2.1 导入url_for方法
from flask import Flask,url_for
2.2 定义别名
通过route方法中的endpoint参数给url定义别名,避免url过长书写不便
@app.route('/index',methods=['GET','POST'],endpoint='dex')
def index():
return 'index页面'
2.3 反向生成url(通过别名)
通过url_for方法反向生成url
url_for('dex') # dex为定义的别名
print(url_for('dex')) # 打印结果:/index
2.4 反向生成url(没有别名的情况下)
如果没有给url定义别名,那么可以直接通过视图函数名来反向生成URL
@app.route('/showalias')
def showaliasabc():
print(url_for('showaliasabc')) # 如果不起别名,默认可以通过函数名反向生成URL
return '别名展示页面'
# 打印结果:/showalias
2.5 示例
代码示例
from flask import Flask,render_template,redirect,url_for
app = Flask(__name__)
@app.route('/index',methods=['GET','POST'],endpoint='dex')
def index():
return 'index页面'
@app.route('/login',methods=['GET','POST'],endpoint='in')
def login():
return 'login页面'
@app.route('/logout',methods=['GET','POST'])
def logout():
return 'logout页面'
@app.route('/showalias')
def showaliasabc():
print(url_for('dex'))
print(url_for('in'))
print(url_for('logout'))
print(url_for('showaliasabc')) # 如果不起别名,默认就是函数名
return '别名展示页面'
if __name__ == '__main__':
app.run()
打印结果
127.0.0.1 - - [29/Mar/2020 17:25:59]
"/index
/login
/logout
/showalias
GET /showalias HTTP/1.1" 200 -
3. 自定义路由转换器
3.1 路由转换初识
见如下代码:
from flask import Flask,render_template,redirect,url_for
app = Flask(__name__)
@app.route('/index/<int:nid>',methods=['GET','POST'])
def index(nid):
print(nid,type(nid))
return "index页面"
if __name__ == '__main__':
app.run()
通过http://127.0.0.1:5000/index/12
访问web,查看后台打印内容:
12 <class 'int'>
127.0.0.1 - - [29/Mar/2020 18:37:33] "GET /index/12 HTTP/1.1" 200 -
注:
<int:nid>
决定了url带参必须是int
类型的,否则会报错
3.2 常用的路由格式
如下,是常用的路由格式
@app.route('/user/<username>') # 字符串
@app.route('/post/<int:post_id>') # int类型
@app.route('/post/<float:post_id>') # float浮点型
@app.route('/post/<path:path>') # 路径
@app.route('/login',methods=['GET','POST']) # 默认不传
常用路由系统有以上五种,所有的路由系统都是基于以下对应关系(也就是路由转换器)来处理的:
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
比如说上例中中的<int:nid>
,url本身是字符串格式,传参也是字符串,但是传递给视图函数的参数的数据类型却是int
类型,这就是通过路由转换器IntegerConverter
来实现的
默认情况下,Flask的路由系统也就以上几种路由转换器
补充:UUID
# UUID
- UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写
- 简单说就是通过一系列算法,得到一个唯一值,在想要通过唯一值标记的情况下可以考虑用UUID
- python可以通过导入uuid模块,然后通过uuid.uuid4()方法得到uuid值
3.3 自定义正则路由匹配
由于Flask默认仅支持以上几种URL的书写方式,但是实际情况中我们可能对URL有更复杂的要求;Flask是支持自定义正则的方式来实现路由匹配的
from flask import Flask,url_for
from werkzeug.routing import BaseConverter
app = Flask(__name__)
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
:param value:
:return:
"""
return int(value) # 此处将参数强转为int类型传递给视图函数,如果想要传递原生参数,可以不进行转换
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
:param value:
:return:
"""
val = super(RegexConverter, self).to_url(value)
return val # 在反解时,也可以return固定的值,当然基本不会这样做
# 添加到flask中;并命名该转换器名称为regex
app.url_map.converters['regex'] = RegexConverter
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
print(nid,type(nid))
print('999:',url_for('index',nid=999))
print('nid:',url_for('index',nid=nid))
return "index页面"
if __name__ == '__main__':
app.run()
访问http://127.0.0.1:5000/index/18
,后台打印结果如下:
18 <class 'int'>
127.0.0.1 - - [29/Mar/2020 19:22:45] "999: /index/999
nid: /index/18
GET /index/18 HTTP/1.1" 200 -
通过以上办法,Flask的路由系统中即可支持正则表达式了
4. app.route参数详解
常用参数
rule, 即URL规则,如:'/index'
view_func, 视图函数名称
methods=None, 即该URL支持的请求方式,如:methods=['GET','POST'];
endpoint=None, 即指定该URL的别名,如:endpoint='n1',用于反向生成URL
defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
strict_slashes=None, 对URL最后的 / 符号是否严格要求
redirect_to=None, 重定向到指定地址
subdomain=None, 子域名访问
default
比如,当URL中没传参数,但是可以直接定义默认参数(如db=mysql)传递给视图函数
from flask import Flask
app = Flask(__name__)
@app.route('/index',methods=['GET','POST'],endpoint='idx',defaults={'db':'mysql'})
def index(db):
print(db)
return "index页面"
if __name__ == '__main__':
app.run()
strict_slashes=None
对URL最后的"/"符号是否严格要求
@app.route('/index',strict_slashes=True)
# 当该参数为True时,仅能访问 http://127.0.0.1:5000/index ;
@app.route('/index',strict_slashes=False)
# 当该参数为False时,可访问 http://127.0.0.1:5000/index 或 http://127.0.0.1:5000/index/
redirect_to=None
重定向到指定的URL地址
from flask import Flask
app = Flask(__name__)
@app.route('/oldfunc/<int:nid>',redirect_to='/newfunc/<nid>')
def oldfunc(nid):
print(nid)
return '老页面'
@app.route('/newfunc/<int:nid>')
def newfunc(nid):
print(nid)
return '新页面'
if __name__ == '__main__':
app.run()
如上,访问http://127.0.0.1:5000/oldfunc/2
时,会自定跳转到http://127.0.0.1:5000/newfunc/2
subdomain=None
子域名访问
from flask import Flask
app = Flask(__name__)
app.config['SERVER_NAME'] = 'mlg.com:5000'
@app.route('/index',subdomain="<username>")
def index(username):
print(username)
return '获取子域名'
if __name__ == '__main__':
app.run()
如上,可以获取到mlg
这个子域名
5. 总结
@app.route('/index') -->
route函数->decorator->add_url_rule -->
add_url_rule函数-> rule = self.url_rule_class()->self.url_map.add(rule) -->
url_map->self.url_map = self.url_map_class() -->
url_map_class()->url_map_class = Map-->
class Map:->self._rules = []
也就是说:实例化后的Map,就相当于一个列表
map = [
rule('/url01',函数1),
rule('/url02',函数2),
rule('/url03',函数3),
]
路由信息,会被添加到上述的列表中,用于url与视图函数的映射
注:无论是Django还是Flask,均是以路由关系创建为开始
-----
重点:
- url
- methods 允许的请求方法
- endpoint url别名(默认是函数名)
- @app.route('/index/<int:nid>/<int:nid2>')
- url_for