Flask 访问请求数据
对于 Web 应用,与客户端发送给服务器的数据交互至关重要。在 Flask 中 由全局的 request 对象来提供这些信息。如果你有一定的 Python 经验,你会好奇,为什么这个对象是全局的,为什么 Flask 还能保证 线程安全。答案是环境作用域:
环境局部变量
内幕
如果你想理解其工作机制及如何利用环境局部变量实现自动化测试,请阅 读此节,否则可跳过。
Flask 中的某些对象是全局对象,但却不是通常的那种。这些对象实际上是特定 环境的局部对象的代理。虽然很拗口,但实际上很容易理解。
想象一下处理线程的环境。一个请求传入,Web 服务器决定生成一个新线程( 或者别的什么东西,只要这个底层的对象可以胜任并发系统,而不仅仅是线程)。 当 Flask 开始它内部的请求处理时,它认定当前线程是活动的环境,并绑定当 前的应用和 WSGI 环境到那个环境上(线程)。它的实现很巧妙,能保证一个应 用调用另一个应用时不会出现问题。
所以,这对你来说意味着什么?除非你要做类似单元测试的东西,否则你基本上 可以完全无视它。你会发现依赖于一段请求对象的代码,因没有请求对象无法正 常运行。解决方案是,自行创建一个请求对象并且把它绑定到环境中。单元测试 的最简单的解决方案是:用 test_request_context() 环 境管理器。结合 with 声明,绑定一个测试请求,这样你才能与之交互。下面 是一个例子:
from flask import request
with app.test_request_context('/hello', method='POST'):
# now you can do something with the request until the
# end of the with block, such as basic assertions:
assert request.path == '/hello'
assert request.method == 'POST'
另一种可能是:传递整个 WSGI 环境给 request_context() 方法:
from flask import request
with app.request_context(environ):
assert request.method == 'POST'
请求对象
API 章节对请求对象作了详尽阐述(参见 request ),因此这 里不会赘述。此处宽泛介绍一些最常用的操作。首先从 flask 模块里导入它:
from flask import request
当前请求的 HTTP 方法可通过 method 属性来访问。通 过:attr:~flask.request.form 属性来访问表单数据( POST 或 PUT 请求 提交的数据)。这里有一个用到上面提到的那两个属性的完整实例:
@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'
# the code below is executed if the request method
# was GET or the credentials were invalid
return render_template('login.html', error=error)
当访问 form 属性中的不存在的键会发生什么?会抛出一个特殊的 KeyError 异常。你可以像捕获标准的 KeyError 一样来捕获它。 如果你不这么做,它会显示一个 HTTP 400 Bad Request 错误页面。所以,多数 情况下你并不需要干预这个行为。
你可以通过 args 属性来访问 URL 中提交的参数 ( ?key=value ):
searchword = request.args.get('q', '')
我们推荐用 get 来访问 URL 参数或捕获 KeyError ,因为用户可能会修 改 URL,向他们展现一个 400 bad request 页面会影响用户体验。
欲获取请求对象的完整方法和属性清单,请参阅 request 的 文档。
文件上传
用 Flask 处理文件上传很简单。只要确保你没忘记在 HTML 表单中设置 enctype="multipart/form-data" 属性,不然你的浏览器根本不会发送文件。
已上传的文件存储在内存或是文件系统中一个临时的位置。你可以通过请求对象 的 files 属性访问它们。每个上传的文件都会存储在 这个字典里。它表现近乎为一个标准的 Python file 对象,但它还有 一个 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 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,用 响应对象的 set_cookie 方法来设置 Cookies。请 求对象的 cookies 属性是一个内容为客户端提交的 所有 Cookies 的字典。如果你想使用会话,请不要直接使用 Cookies,请参 考 会话 一节。在 Flask 中,已经注意处理了一些 Cookies 安全 细节。
读取 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
可注意到的是,Cookies 是设置在响应对象上的。由于通常视图函数只是返 回字符串,之后 Flask 将字符串转换为响应对象。如果你要显式地转换,你 可以使用 make_response() 函数然后再进行修改。
有时候你想设置 Cookie,但响应对象不能醋在。这可以利用 延迟请求回调 模式实现。
为此,也可以阅读 关于响应 。
更多建议: