上节我们已经安装好了 Flask ,接下来我们就利用 Flask 写一个最简单的示例。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def Hello():
return 'Hello World~'
这个程序做了哪些事情呢:
- 首先导入了 Flask 类,这个类的实例将会成为我们的 WSGI 应用。
- 接下来我们创建了这个类的实例。传入的第一个参数是应用程序的模块名或者包名。__name__ 的值会因为启动的是应用程序(__main__)还是模块(模块名)会有所不同。Flask 需要这些信息来知道去哪里找到模板,静态文件等。
- 然后使用 Route() 装饰器来告诉 Flask 什么 URL 用来引发我们的函数。
- 函数名同样用来为特定的函数产生 URLs,函数用来返回我们想要在浏览器中展示的信息。
将上面的文件保存为 hello.py 或者其他的名字,除了 flask.py 因为这会和 Flask 自身的 flask.py 文件冲突。
运行这个应用,可以使用 flask 命令或者 python 的 -m 转换到 flask,在此之前,需要通过设置 FLASK_APP 将你的应用程序告诉它将要运行的终端。在 windows 中
set FLASK_APP = hello.py
接下来就可以使用 flask 命令或者 python 来启动应用
默认处于debugger模式,此模式能够让用户在电脑上运行任意的 python 代码,但是服务只能在本机上运行。如果要关闭 debugger 模式并让服务公开,只需要添加 flask run --host=0.0.0.0,他会让操作系统监听所有的公共 IP。
如果想要 debug 支持在代码改变的时候服务器自动重载而不是每次都手动重启服务,可以在运行服务之前设置 FLASK_ENV 为development,在 windows 中
set FLASK_ENV = development
flask run
她做了几件事:
- 激活 debugger
- 激活自动重载器
- 在 flask 应用中使能 debug 模式
也可以通过设置 FLASK_DEBUG = 1 来控制 debug 模式。
使用 route() 装饰器把一个函数绑定到 URL 上
@app.route('/')
def index():
return 'Hello'
@app.route('/hello')
def hello():
return 'Hello world'
还可以使用 <variable_name> 将变量部分加到 URL 中,函数可以接受这个变量作为参数。可以使用转换器来确定参数的类型,就像这样 <converter:variable_name>
@app.route('/user/<username>')
def show_user_profile(username):
return 'User %s' % username
@app.route('/post/<int:post_id>')
def show_post(post_id):
return 'Post %d' % post_id
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
return 'Subpath %s' % subpath
转换器一共有以下几种类型
需要注意的是,使用 path 这个转换器的时候,在变量名后面不加斜线,接收到的是不加斜线的参数;加斜线的时候,接收到的是加斜线的参数。但是对于不是 path 的转换器,如果路径中不是以斜线 '/' 结尾的话,而在搜索 URL 的时候加了斜线,就出现 400 Not Found 错误。这也是 URL 的 unique 特性,避免重复搜索。
看下面这2种写法。一种是在末尾加了斜线,一种是没有在末尾加上斜线。
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
规范的 URL 写法后面是有斜线的。如果在搜索 URL 中没有加斜线,第一种写法会让 flask 重定向到标准的 URL,也就是会自动在搜索 URL 的时候后面加上斜线,从而在搜索的时候 URL 后面是否添加斜线都不会报错。但是第二种写法如果在搜索 about 页面的时候 URL 加了斜线,就会报 404 Not Found 错误,这也是 URL 的唯一性所决定的。
我们还可以用 url_for()函数来为某个函数建立 URL,他接受函数名作为它的第一个参数,并且可以接受任意数量的关键字参数,其中的每一个对应到 URL 规则的变量部分,未知的变量部分将会作为查询参数加入到 URL 。
使用 url_for()的理由有以下几个:
- 产生的路径是绝对的,能够避免相对路径的异常行为。
- 能够改变 URLs 而不用记住并手动改变 URLs
我们使用 test_request_context() 函数来试验 url_for() ,它让 Flask 在我们使用 Python shell 时像是处理一个请求一样进行动作
Web应用在搜索不同的 URL 使用不同的 HTTP 方法,默认的是,路径只会响应 GET 请求,你可以使用 route() 装饰器的 method 参数来处理不同的 HTTP 方法。
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return do_the_login()
else:
return show_the_login_form()
动态 Web 应用同样需要静态文件,通常是 JavaScript 和 CSS 文件。要创建静态文件只需要在包里面创建一个文件并取名为 static ,就可以 /satatic 下使用。为静态文件创建 URL 可以使用 url_for() 函数,使用 static 这样一个特殊的端点名称。
url_for('static', filename='style.css')
这个文件必须在文件系统中存储为 static/style.css
Flask 使用为你的应用配置一个 Jinja模板引擎。使用 render_template() 函数来渲染一个模板,你需要提供模板名称以及传给模板引擎作为关键字参数的变量
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
Flask 会在 templates 文件中查找模板,因此如果你的应用是一个模块,这个文件就在模块旁边,如果是一个包它就会在包里
一个模板样例如下
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
在模板里面你同样可以使用 request,session,和 g 对象,还有 get_flashed_messages() 函数。
request 对象记录在 API 部分,要使用首先要从 flask 模块中导入 request。
通过 request 的 method 属性可以获得当前请求的方法。通过 form 属性可以获得表单数据。
@app.route('/login', methods=['POST', 'GET'])
def login():
error = None
if request.method == 'POST':
if valid_login(request.form['username'],
request.form['password']):
return log_the_user_in(request.form['username'])
else:
error = 'Invalid username/password'
return render_template('login.html', error=error)
如果关键字在表单属性中不存在,就会抛出特殊的 KeyError 异常,可以像标准的 KeyError 异常一样对她进行捕获,否则页面就会显示 400 Bad Request。因此大多数情况不需要处理这个问题。
通过 args 属性获得传递到 URL 中的参数 (?key=value)
searchword = request.args.get('key', '')
推荐使用 get 或者捕获 KeyError 获得 URL 参数,因为用户可能改变 URL 或者不当的使用引发 400 Bad Request。request 对象更多的方法和属性可以查阅 Request 文档。
Flask 也可以很容易的处理上传的文件,这个时候需要在 HTML 表单中设置 'enctype="multipart/form-data' ,否则浏览器不会为你发送文件。通过 request 对象中的 file 属性获得这些文件,他就像标准的 python 文件对象一样,但是他有 save() 方法允许你把文件存储到服务器的文件系统。
from flask import request
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/uploaded_file.txt')
...
如果想要知道文件在上传到应用之前在客户端是如何命名的,可以使用 filename 属性。但是其实这个值是可以伪造的。如果想要用客户端的文件名来存储服务器端的文件,通过 Werkzeug 提供的 secure_filename() 函数来传递。
from flask import request
from werkzeug.utils import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename))
关于 Cookies ,可以 通过 cookies 属性获得 cookies 并可以通过 response 对象的 set_cookie 方法设置 cookies。request 对象的 cookies 属性是一个字典,包含了客户端发送的所有 cookies。如果想要使用 sessions,就不要直接使用 cookies 而是使用 Flask 中的 Sessions ,可以为 cookies 增加安全性。
from flask import request
@app.route('/')
def index():
username = request.cookies.get('username')
# use cookies.get(key) instead of cookies[key] to not get a
# KeyError if the cookie is missing.
保存 cookies
from flask import make_response
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
通过 redirect() 可以重定向,使用 abort 加上错误码可以丢弃一个请求
from flask import abort, redirect, url_for
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login')
def login():
abort(401)
this_is_never_executed()
可以使用 errorhandler() 装饰器来定制错误页
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
注意到 render_template 后面的 404 ,他会告诉 flask 那个页面的状态码,404代表 Not Found。200 代表正常。
视图函数的返回值会自动转换为 response 对象。如果返回值是一个字符串,它会转换为字符串作为响应体的 response 对象,或者是一个 200 OK 的状态码或者是一个 文本/HTML mimetype。Flask 转换返回值为 response 对象的逻辑如下:
- 如果返回正确的 response 对象类型,就直接从视图函数返回。
- 如果是一个字符串,就用那个数据和默认参数创建一个 response 对象。
- 如果返回一个元组,并且元组中的元素能够提供额外的信息,这样的元组必须放在表中(response,status,headers) 或者(response,headers) 也就是至少有一个元素放在表中。status 会重写状态码,headers 可以是一个 列表 或者 字典。
- 如果上面情况都没有出现,Flask 会认为返回值是一有效的 WSGI 应用,并将之转换为一个 response 对象。
如果想要控制 view 里面的 response 对象的结果,可以使用 make_response() 函数
@app.errorhandler(404)
def not_found(error):
return render_template('error.html'), 404
例如上面的 view ,你只需要用 make_response() 重写返回表达式,获得 response 对象并修改它,然后将之返回即可。
@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
除了 request 对象还有一种对象那就是 session 对象,它能够从一个请求到另一个请求的特定用户的信息。他以加密的方式在 cookies 上实现,这意味着用户能够看到你的 cookies 的内容但是不能修改它,除非以你知道密码。
要使用 sessions 你必须设置密码
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
这里的 escape() 是用于当你没有使用模板引擎的时候跳出。
一个好的应用必须要处理好用户反馈,Flask 提供了一个简单的方式,也就是 flashing 系统,这个系统在请求最后记录信息,能够且仅能够在下一个请求获得它,这通常是和布局模板结合起来暴露这个信息。通常需要使用的函数就是 flash() 以及如果想要控制这个信息要使用的 get_flash_messages() 函数
如果想要记录日志信息,可以使用 logger
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
如果想要在应用中添加 WSGI 中间件,你也可以覆盖掉内置的 WSGI 应用
from werkzeug.contrib.fixers import LighttpdCGIRootFix
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)
此外,Flask 还有许多扩展,这些扩展通常是一些包能够让你完成一些通用的任务。例如,Flask-SQLAlchemy 提供了 SQLAlchemy 简单易用的 Flask支持。