现代 web 应用都使用有意义的 URL ,这样有助于用户记忆,网页会更得到用户的青睐, 提高回头率。
在Flask中可以使用 route() 装饰器来把函数绑定到 URL。
定义一个简单的路由:
@app.route('/') def hello_world(): return 'Hello World!'此时我们在浏览器中访问的地址就是:http://127.0.0.1:5000/
我们可以通过把 URL 的一部分标记为 <variable_name> 就可以在 URL 中添加变量。标记的 部分会作为关键字参数传递给函数。 此时函数参数的变量名必须和variable_name名称一致。
@app.route('/user/<username>') def get_user(username): return f"接收到的用户名为:{username}"浏览器访问该地址传入参数username:
其中f’{username}'语法为3.6后的新特性等价于’ {username} '.format(username=username)
默认情况下,服务器只会监听Get请求,我们可以使用postman进行模拟测试: 状态码 405 Method Not Allowed 表明服务器禁止了使用当前 HTTP 方法的请求。也就是说此时服务器是不支持该访问使用post方式的。那么如何制定请求方式呢?
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上面是flask源码,可以看到我们可以使用数组来制定支持的请求。
可以看到,其中的options参数为werkzeug.routing.Rule对象,那么我们在一起来看看这个对象有哪些属性可以设置:
def __init__( self, string, defaults=None, subdomain=None, methods=None, build_only=False, endpoint=None, strict_slashes=None, merge_slashes=None, redirect_to=None, alias=False, host=None, websocket=False, ):这里截取初始化函数的一段代码,可以看到确实有methods属性。看到这里大家好应该知道有哪些东西可以在这里作为参数了。
那么对于开发Restful API规范来说,我们常用的有: 所以我们可以这样来指定请求方法:
@app.route('/user/<username>', methods=['GET', 'POST','PUT','DELETE'])其中’GET’是不区分大小写的,其内部做了转换处理:
methods = set(item.upper() for item in methods)其中上述写法为python3中的列表推导式写法,适合用于简单的操作。格式为:
格式:[表达式 for 变量 in 旧列表] or [表达式 for 变量 in 旧列表 if 条件]列表推导式提供了从序列创建列表的简单途径。通常应用程序将一些操作应用于某个序列的每个元素,用其获得的结果作为生成新列表的元素,或者根据确定的判定条件创建子序列。
每个列表推导式都在 for 之后跟一个表达式,然后有零到多个 for 或 if 子句。返回结果是一个根据表达从其后的 for 和 if 上下文环境中生成出来的列表。如果希望表达式推导出一个元组,就必须使用括号。
此时我们就可以通过post等方法访问:http://127.0.0.1:5000/user/zhangsan
注意这里URL中的变量可以理解为正则匹配,变量之间要有明确的区分,这样才会得到期望的结果,如@app.route('/user/<username><password>/')可能得到让你预料不到的结果。 转化器 类型描述string(缺省值) 接受任何不包含斜杠的文本int接受正整数float接受正浮点数path类似 string ,但可以包含斜杠uuid接受 UUID 字符串正则表达式如果还有不太明白的可参阅官方文档:https://docs.python.org/zh-cn/3/howto/regex.html#regex-howto
一个简单的例子:
@app.route('/post/<int:post_id>') def show_post(post_id): # show the post with the given id, the id is an integer return 'Post %d' % post_id如果我们传入非数字类型的参数,此时会出现404错误:
接下来我们看下Flask源码中关于转化器是怎么实现的,其位于\venv\Lib\site-packages\werkzeug\routing.py
首先其内置的转化器有:
#: the default converter mapping for the map. DEFAULT_CONVERTERS = { "default": UnicodeConverter, "string": UnicodeConverter, "any": AnyConverter, "path": PathConverter, "int": IntegerConverter, "float": FloatConverter, "uuid": UUIDConverter, }我们在看下UnicodeConverter转化器的实现:
class BaseConverter(object): """Base class for all converters.""" regex = "[^/]+" weight = 100 def __init__(self, map): self.map = map def to_python(self, value): return value def to_url(self, value): if isinstance(value, (bytes, bytearray)): return _fast_url_quote(value) return _fast_url_quote(text_type(value).encode(self.map.charset)) class UnicodeConverter(BaseConverter): """This converter is the default converter and accepts any string but only one path segment. Thus the string can not include a slash. This is the default validator. Example:: Rule('/pages/<page>'), Rule('/<string(length=2):lang_code>') :param map: the :class:`Map`. :param minlength: the minimum length of the string. Must be greater or equal 1. :param maxlength: the maximum length of the string. :param length: the exact length of the string. """ def __init__(self, map, minlength=1, maxlength=None, length=None): BaseConverter.__init__(self, map) if length is not None: length = "{%d}" % int(length) else: if maxlength is None: maxlength = "" else: maxlength = int(maxlength) length = "{%s,%s}" % (int(minlength), maxlength) self.regex = "[^/]" + length class IntegerConverter(NumberConverter): """This converter only accepts integer values:: Rule("/page/<int:page>") By default it only accepts unsigned, positive values. The ``signed`` parameter will enable signed, negative values. :: Rule("/page/<int(signed=True):page>") :param map: The :class:`Map`. :param fixed_digits: The number of fixed digits in the URL. If you set this to ``4`` for example, the rule will only match if the URL looks like ``/0001/``. The default is variable length. :param min: The minimal value. :param max: The maximal value. :param signed: Allow signed (negative) values. .. versionadded:: 0.15 The ``signed`` parameter. """ regex = r"\d+" num_convert = int详细的代码我们了解一下即可,如果在实际的项目开发中遇到内置的转化器无法满足需求的时候,我们就可以考虑使用自定义的转化器。参照上面的代码可以知道,我们首先需要定义一个自己的转化器类,继承自BaseConverter,最简单的方式可以参考IntegerConverter转化器,只要我们重新指定regex 的正则表达式即可:
# 匹配11位数字的手机号码 class PhoneConverter(BaseConverter): regex = r'1[85734]\d{9}'虽然我们自定义了我们的转化器,但是必须的告诉Flask,这是我们的转化器,你的按照我的规则来。此时我们需要将自己的转化器注册到url_map中去。
在Flask类的定义中有url_map_class 属性,其初始值为werkzeug.routing.Map类型:
#: The map object to use for storing the URL rules and routing #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`. #: #: .. versionadded:: 1.1.0 url_map_class = Mapwerkzeug.routing.Map类:
... def __init__( self, rules=None, default_subdomain="", charset="utf-8", strict_slashes=True, merge_slashes=True, redirect_defaults=True, converters=None, sort_parameters=False, sort_key=None, encoding_errors="replace", host_matching=False, ): ... :param converters: A dict of converters that adds additional converters to the list of converters. If you redefine one converter this will override the original one.大家可以清楚的看到其中有一个converters的属性,这个就是我们需要的。该属性是一个字典,所以添加自定义的转化器也就很简单了。
# 匹配11位数字的手机号码 class PhoneConverter(BaseConverter): regex = r'1[85734]\d{9}' app.url_map.converters['phone'] = PhoneConverter @app.route('/') def hello_world(): return 'Hello World!' @app.route('/user/<phone:phone_number>') def get_user_by_phone(phone_number): # show the post with the given id, the id is an integer return 'User phone number is : %s' % phone_number 唯一的 URL / 重定向行为在Flask中我们需要注意这样的一条规则:是否使用尾部的斜杠。看个示例:
@app.route('/user/<phone:phone_number>') def get_user_by_phone(phone_number): # show the post with the given id, the id is an integer return 'User phone number is : %s' % phone_number @app.route('/admin/<phone:phone_number>/') def get_adin_user_by_phone(phone_number): # show the post with the given id, the id is an integer return 'Admin User phone number is : %s' % phone_number这两个URL唯一不同的地方就在于/admin/<phone:phone_number>/使用了尾部斜杠。在Flask里面访问一个没有斜杠结尾的 URL 时 Flask 会自动进行重定向,帮你在尾部加上一个斜杠。前提是你定义路由的时候在尾部添加了斜杠。
/user/<phone:phone_number>的 URL 没有尾部斜杠,因此其行为表现与一个文件类似。如果访问这个 URL 时添加了尾部斜杠就会得到一个 404 错误。这样可以保持 URL 唯一,并帮助 搜索引擎避免重复索引同一页面。
URL 构建url_for() 函数用于构建指定函数的 URL。它把函数名称作为第一个 参数。它可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量。未知变量 将添加到 URL 中作为查询参数。
为什么不在把 URL 写死在模板中,而要使用反转函数 url_for() 动态构建?
反转通常比硬编码 URL 的描述性更好。你可以只在一个地方改变 URL ,而不用到处乱找。URL 创建会为你处理特殊字符的转义和 Unicode 数据,比较直观。生产的路径总是绝对路径,可以避免相对路径产生副作用。如果你的应用是放在 URL 根路径之外的地方(如在 /myapplication 中,不在 / 中), url_for() 会为你妥善处理。上示例:
@app.route('/') def hello_world(): print(url_for('url_for_test')) print(url_for('get_user_by_phone', phone_number='15928452154')) print(url_for('get_admin_user_by_phone', phone_number='15928452154')) return 'Hello World!' app.route('/user/<phone:phone_number>') def get_user_by_phone(phone_number): # show the post with the given id, the id is an integer return 'User phone number is : %s' % phone_number @app.route('/admin/<phone:phone_number>/') def get_admin_user_by_phone(phone_number): # show the post with the given id, the id is an integer return 'Admin User phone number is : %s' % phone_number @app.route('/urlfor/') def url_for_test(): return "url for test"运行结果: 如果我们没有给get_user_by_phone指定其对应的参数,那么我们会得到如下错误信息: 也就是说url_for函数在使用时如果函数有参数,那么必须指定对应的参数。如果这个参数之前没有在’url’中定义,那么将变成查询字符串的形式放到’url’中。
print(url_for('get_user_by_phone',phone_number='15928452154',test='test_params'))另外一个就是字符转义:
print(url_for('get_user_by_phone',phone_number='15928452154',test='test_params',email='liu@34.com'))