Flask 访问请求数据

2021-08-10 11:44 更新

对于 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 属性来访问表单数据( POSTPUT 请求 提交的数据)。这里有一个用到上面提到的那两个属性的完整实例:

@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,但响应对象不能醋在。这可以利用 延迟请求回调 模式实现。

为此,也可以阅读 关于响应


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号