flask简介
flask是一个相对于django而言轻量级的web框架。
和django大包大揽不同,flask建立于一系列的开源软件包之上,这其中 最主要的是wsgi应用开发库werkzeug和模板引擎jinja:
策略 :werkzeug和jinja这两个库和flask一样,都是pocoo团队开发的。这 或许体现了pocoo与django竞争时关于生态的一种策略,这种策略的自然 延伸是flask框架中没有包含数据库方面的构件,无论orm还是其他。
关注点 :flask是一个wsgi应用框架,这意味着我们进行flask开发时,不需要 关注网络方面的操作,flask应用的入口是封装过的网络请求包,出口是 网络响应,我们仅需要关注这个阶段内的处理逻辑。
wsgi服务器 :flask虽然内置了简单的wsgi服务器,但其性能仅仅适用于开发期的调试。 flask官网推荐了多种wsgi服务器,实现方式从多进程到多线程到协程, 这方面的选择我们在本课程中将不涉及。
rest适应性 :虽然flask和django一样,最初的出发点都是服务端的动态网页应用。但 flask的设计使之也相当适用于面向资源的rest架构,在越来越移动化 并且单页应用越来越重要的web开发领域,这是flask相对于django相当 大的优势。
hello flask
编写一个基于flask的hello world相当容易:
1、导入flask类
from flask import flask
flask类是flask框架的核心类,它实现了wsgi应用规范。
2、创建flask实例
app = flask(__name__)
flask构造函数的第一个参数指定一个引入名/importname。flask框架 使用这个名字进行静态资源、模板、错误信息的定位。除非你清楚的理解它的 作用,通常情况下,我们总应该使用特殊变量_name。
flask实例是可调用的(具有call方法),这个实例可以直接对接 wsgi服务器。
3、注册路由
@route(‘/’)
def index():
return ‘hello,flask!’
注册路由就是建立url规则和处理函数之间的关联。flask框架依赖于路由 完成http请求的分发。
路由中的函数被称为视图函数,其返回值将作为http响应的正文内容。
4、对接并启动wsgi服务器
flask封装了一个简单的开发用wsgi服务器,我们可以通过调用run() 启动服务器运行:
app.run(host=’0.0.0.0′,port=80)
概述
路由是mvc架构的web框架中相当重要的一个概念,也是本节课程的重点。
顾名思意,路由就是在迷茫中找出一条路的意思。在flask框架中,路由就表示为用户请求的url找出其对应的处理函数之意。
在本节课程,我们将主要从以下几个方面讲解flask框架中的路由:
如何为应用注册路由? 如何为路由指定其支持的http方法? 如何匹配动态url? 如何对url中的变量类型进行过滤? 如何理解访问点/endpoint? 如何为应用设定静态路由? 如何避免硬编码指向其他视图的url?
注册路由
在flask应用中,路由是指用户请求的url与视图函数之间的映射。flask框架 根据http请求的url在路由表中匹配预定义的url规则,找到对应的视图函数, 并将视图函数的执行结果返回wsgi服务器:
可见路由表在flask应用中处于相当核心的位置。路由表的内容是由应用开发者填充。
route装饰器 :可以使用flask应用实例的route装饰器将一个url规则绑定到 一个视图函数上。
例如,下面的示例将url规则/test绑定到视图函数test()上:
@app.route(‘/test’)
def test():
return ‘this is response’
如果这个应用部署在主机ezhost.com的根目录下,那么当用户访问:
http://pythontab.com/teset
flask框架就会调用我们的test()函数,其返回结果就传递给wsgi服务器发送给访问者。
add_url_rule() :另一种等价的写法是使用flask应用实例的add_url_route()方法。 下面的示例注册了一个与前例相同的路由:
def test():
return ‘this is response’
app.add_url_route(‘/test’,view_func=test)
其实,route装饰器内部也是通过调用add_url_route()方法实现的路由注册。 但是显然,使用装饰器使代码看起来更优雅一些。
为路由指定http方法
默认情况下,flask路由仅支持http的get请求。可以使用methods关键字参数,在注册 路由时显式地声明视图方法支持的http方法。
例如,下面的示例将url规则/auth绑定到视图函数v_auth(),这个路由仅支持post方法:
@app.route(‘/auth’,methods=[‘post’])
def v_auth():pass
指定多种http方法支持
关键字参数methods的类型为list,因此可以同时指定多种http方法。
下面的示例中,使url规则/user同时支持post方法和get方法:
@app.route(‘/user’,methods=[‘post’,’get’])
def v_users():
if request.method == ‘get’:
return … # 返回用户列表
if request.method == ‘post’
return … #创建新用户
这个特性使flask非常易于开发rest架构的后台服务,而不仅仅局限于传统的动态网页。
匹配动态url
有时我们需要将同一类url映射到同一个视图函数处理,比如,使用同一个视图函数 来显示不同用户的个人档案。我们希望以下的url都可以分发到同一个视图函数:
在flask中,可以将url中的可变部分使用一对小括号声明为变量, 并为视图函数声明同名的参数:
@app.route(‘/user/’)
def v_user(uname):
return ‘%s\’s profile’ % uname
在上面的示例中,url规则中的表示这部分是可变的,flask将提取用户请求的 url中这部分的内容,并作为视图函数v_user()的uname参数进行调用。
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)
f = open(fullname)
cnt = f.read()
f.close()
return cnt
测试结果表明,/file/a.txt和/file/b.txt都没有问题,但是/file/repo/c.txt和 /file/repo/d.txt却会失败。
这是因为,默认情况下,在url规则中的变量被视为不包含/的字符串。/file/repo/c.txt 是没有办法匹配url规则/file/的。
可以使用内置的path转换器告诉flask框架改变这一默认行为。path转换器允许 规则匹配包含/的字符串:
@app.route(‘/file/’)
在flask中,转换器/converter用来对从url中提取的变量进行预处理,这个过程 发生在调用视图函数之前。flask预置了四种转换器:
string – 匹配不包含/的字符串,这是默认的转换器
path – 匹配包含/的字符串
int – 只有当url中的变量是整型值时才匹配,并将变量转换为整型
float – 只有当url中的变量是浮点值时才匹配,并将变量转换为浮点型
访问点/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关键字参数改变这一默认行为:
1
2
@app.route(‘/home’,endpoint=’whocare’)
def home():pass
此时的两张路由表将变成这样:
静态目录路由
当创建应用实例时,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 | +————————————————————+
构造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。下面的示例将生成urlhttp:///contacts:
@app.route(‘/’)
def v_index():
print url_for(‘v_contacts’,_external=true)
@app.route(‘/contact’)
def v_contacts():pass