flask -- 路由与视图函数

原文链接

1. 概述

路由 是MVC架构的Web框架中相当重要的一个概念,也是本节课程的重点。顾名思意,路由就是在迷茫中找出一条路的意思。在Flask框架中,路由 就表示为用户请求的URL找出其对应的处理函数之意。

在本节课程,我们将主要从以下几个方面讲解Flask框架中的路由:

  • 如何为应用注册路由?

  • 如何为路由指定其支持的HTTP方法?

  • 如何匹配动态URL?

  • 如何对URL中的变量类型进行过滤?

  • 如何理解访问点/endpoint?

  • 如何为应用设定静态路由?

  • 如何避免硬编码指向其他视图的URL?

现在开始吧。

2. 注册路由

在Flask应用中,路由是指用户请求的URL与视图函数之间的映射。Flask框架 根据HTTP请求的URL在路由表中匹配预定义的URL规则,找到对应的视图函数, 并将视图函数的执行结果返回WSGI服务器:

可见路由表在Flask应用中处于相当核心的位置。路由表的内容是由应用开发者填充。

route装饰器 :可以使用Flask应用实例的route装饰器将一个URL规则绑定到 一个视图函数上。

例如,下面的示例将URL规则/test绑定到视图函数test()上:

@app.route('/test')
def test():
  return 'this is response'
  • 1
  • 2
  • 3

Flask框架就会调用我们的test()函数,其返回结果就传递给WSGI服务器发送给访问者。

add_url_rule() :另一种等价的写法是使用Flask应用实例的add_url_rule()方法。 下面的示例注册了一个与前例相同的路由:

def test():
  return 'this is response'
app.add_url_rule('/test',view_func=test)
  • 1
  • 2
  • 3

其实,route装饰器内部也是通过调用add_url_rule()方法实现的路由注册。 但是显然,使用装饰器使代码看起来更优雅一些。

实验代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from flask import Flask 
app = Flask(__name__)

@app.route('/')
def index():
    return '''
    
        
  • test
  • friend
  • ''' @app.route('/test') def test(): return 'this is response' @app.route('/friend') def friend(): return '

    My Friend List

    ' app.run(host = '0.0.0.0', port = '8080')

    实验页面如下:

    3. 为路由指定HTTP方法

    默认情况下,Flask路由仅支持HTTP的GET请求。可以使用methods关键字参数,在注册 路由时显式地声明视图方法支持的HTTP方法。

    例如,下面的示例将URL规则/auth绑定到视图函数v_auth(),这个路由仅支持POST方法:

    @app.route('/auth', methods = ['POST'])
    def v_auth(): pass

    3.1 指定多种HTTP方法支持

    关键字参数methods的类型为list,因此可以同时指定多种HTTP方法。

    下面的示例中,使URL规则/user同时支持POST方法和GET方法:

    @app.route('/user', methods = ['POST', 'GET'])
    def v_users():
      if request.method == 'GET':
        return ... # 返回用户列表
      else:
        return ... # 创建新用户

    这个特性使Flask非常易于开发REST架构的后台服务,而不仅仅局限于传统的动态网页。

    实验代码如下:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    from flask import Flask 
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return '''
            
    ''' @app.route('/auth',methods=['POST']) def v_auth(): return '

    在这里做验证工作

    ' app.run(host = '0.0.0.0', port = '8080')

    实验页面如下:

    4. 匹配动态URL

    有时我们需要将同一类*URL映射到同一个*视图函数处理,比如,使用同一个视图函数 来显示不同用户的个人档案。我们希望以下的URL都可以分发到同一个视图函数:

    在Flask中,可以将URL中的可变部分使用一对小括号<>声明为变量, 并为视图函数声明同名的参数:

    @app.route('/user/')
    def v_user(uname):
      return '%s\'s Profile' % uname

    在上面的示例中,URL规则中的表示这部分是可变的,Flask将提取用户请求的 URL中这部分的内容,并作为视图函数v_user()的uname参数进行调用。

    实验代码如下:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    from flask import Flask 
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return '''
        
            
  • here
  • ''' @app.route('/user//friend/') def u_f(uname, fname): return '%s\'s friend - %s\'s profile' % (uname, fname) app.run(host = '0.0.0.0', port = '8080')

    实验页面如下:

    5. URL变量类型过滤

    考虑下面的示例,我们希望通过HTTP共享文件夹./var/readonly中的文件:

    /var
     /readonly
       /a.txt
       /b.txt
       /repo
         /c.txt
         /d.txt
    

    简单思考一下就有答案了。我们可以构造URL规则/file/,然后直接 读取文件内容返回给用户。注册如下的路由:

    @app.route('/file/')
    def v_file(fname):
    
        fullname = os.path.join('./var/readonly/',fname)
        #print fullname
        f = open(fullname, 'r')
        data = f.readlines()
        f.close()
        return "%s" % data

    测试结果表明,/file/a.txt和/file/b.txt都没有问题,但是/file/repo/c.txt和 /file/repo/d.txt却会失败。

    127.0.0.1 - - [03/Nov/2016 20:15:01] "GET /file/a.txt HTTP/1.1" 200 -
    127.0.0.1 - - [03/Nov/2016 20:21:18] "GET /file/b.txt HTTP/1.1" 200 -
    127.0.0.1 - - [03/Nov/2016 20:21:24] "GET /file/repo/c.txt HTTP/1.1" 404 -

    这是因为,默认情况下,在URL规则中的变量被视为不包含/的字符串。/file/repo/c.txt 是没有办法匹配URL规则/file/的。

    可以使用内置的path转换器告诉Flask框架改变这一默认行为。path转换器允许 规则匹配包含/的字符串:

    @app.route('/file/')

    在Flask中,转换器/converter用来对从URL中提取的变量进行预处理,这个过程 发生在调用视图函数之前。Flask预置了四种转换器:

    • string - 匹配不包含/的字符串,这是默认的转换器
    • path - 匹配包含/的字符串
    • int - 只有当URL中的变量是整型值时才匹配,并将变量转换为整型
    • float - 只有当URL中的变量是浮点值时才匹配,并将变量转换为浮点型

    6. 访问点/endpoint

    我们一直强调,路由的作用是根据请求的URL,找到对应的视图函数。这没错,但是在 Flask框架中,请求任务的分发并不是直接从用户请求的URL*一步定位到视图函数, 两者之间隔着一个访问点/endpoint*。

    以下面的代码为例,我们看Flask怎样实现请求的分发:

    @app.route('/home')
    def home(): pass

    在Flask内部使用两张表维护路由:

    • url_map :维护URL规则和endpoint的映射
    • view_functions :维护endpoint和视图函数的映射。

    以用户访问URL*/home*为例,Flask将首先利用url_map找到所请求URL对应的 endpoint,即访问点home,然后再利用view_functions表查找home这个访问点 对应的视图函数,最终匹配到函数home():

    默认访问点 :当我们使用route装饰器注册路由时,默认使用被装饰函数的 函数名(name)作为访问点,因此,你看到上面的表中,路由中的访问点为home。

    自定义访问点 :可以在使用route装饰器或调用add_url_rule()方法注册路由时,使用 endpoint关键字参数改变这一默认行为:

    @app.route('/home',endpoint='whocare')
    def home():pass

    此时的两张路由表将变成这样:

    7. 静态目录路由

    当创建应用实例时,Flask将自动添加一条静态目录路由,其访问点 始终被设置为static,URL规则默认被设置为/static,本地路径默认被 设置为应用文件夹下的static子文件夹:

    +------------------------------------------------------------+
    | url rule | endpoint | view_function |
    |------------------------------------------------------------|
    | /static | static | Flask.send_static_file |
    +------------------------------------------------------------+

    如果你的应用目录如下:

    /app
     /web.py
     /static
       /main.css
       /jquery.min.js 

    那么启动应用后就可以通过URL*/static/main.css*访问static文件夹下的main.css了。

    除了访问点被固定为static,静态目录的URL规则和本地目录都是可以根据应用情况进行调整。

    改变默认的本地路径 :可以在创建应用对象时使用关键字参数static_folder改变 默认的静态文件夹。例如,你的静态文件都存放在应用下的assets目录下, 那么可以按如下的方式创建应用对象:

    app = Flask(__name__,static_folder='assets')
    

    也可以使用一个绝对路径:

    app = Flask(__name__,static_folder='/var/www/static')
    

    改变默认的本地路径并不会对路由表产生影响。

    改变默认的URL规则 : 如果不喜欢静态目录URL*/static*,也可以在创建应用 对象时使用关键字参数static_url_path换一个别的名字。 
    下面的示例中,将应用下的assets文件夹注册为静态目录/assets:

    app = Flask(__name__,static_folder='assets',static_url_path='/assets')

     

    当应用运行后,通过URL*/assets/main.css*就可以访问assets文件夹下的 main.css文件了。

    这时的路由表变化为:

    +------------------------------------------------------------+
    | url | endpoint | view_function |
    |------------------------------------------------------------|
    | /assets | static | Flask.send_static_file |
    +------------------------------------------------------------+

    8. 构造URL

    在一个实用的视图中,不可避免地存在指向其他视图的链接。在之前的课程示例中,我们 都是在视图函数中这样硬编码这些链接URL的:

    @app.route('/')
    def v_index():
       return 'tech'
    @app.route('/tech') 
    def v_tech():pass

    大部分情况下这种硬编码URL是可以工作的。但如果这个应用被挂在WSGI服务器的一个 子路径下,比如:/app1,那么用户访问URL*/tech*是不会成功的,这时应当访问/app1/tech 才可以正确地路由到视图函数v_tech()。

    我们应当使用访问点让Flask框架帮我们计算链接URL。简单地给url_for()函数传入 一个访问点,它返回将是一个可靠的URL地址:

    @app.route('/')
    def v_index():
     print url_for('v_contacts') # /contact
     return 'see console output!'
    @app.route('/contact')
    def v_contacts():pass

    添加查询参数 : 使用关键字参数,可以在构造的URL中生成查询串。下面的调用将生成 /contact?format=json

    @app.route('/')
    def v_index():
     print url_for('v_contacts',format='json')
     return ''
    @app.route('/contact') 
    def v_contacts():pass

    添加URL变量 : 如果指定访问点对应的视图函数接收参数,那么关键字参数将生成对应的参数URL。下面的 示例将生成/contact/Julia?format=html:

    @app.route('/')
    def v_index():
     print url_for('v_contact',name='Julia',format='html')
     return ''
    @app.route('/contact/')
    def v_contact(name):pass

    添加锚点 :使用_anchor关键字可以为生成的URL添加锚点。下面的示例将生成URL /contact#part2

    @app.route('/')
    def v_index():
     print url_for('v_contacts',_anchor='part2')
    @app.route('/contact')
    def v_contacts():pass

    外部URL : 默认情况下,url_for()生成站内URL,可以设置关键字参数_external 为True,生成包含站点地址的外部URL。下面的示例将生成URL*http://

    @app.route('/')
    def v_index():
     print url_for('v_contacts',_external=True)
    @app.route('/contact')
    def v_contacts():pass
    

    你可能感兴趣的:(flask)