如何在Flask中处理传入的请求数据

来自菜鸟教程
跳转至:导航、​搜索

介绍

Web 应用程序经常需要处理来自用户的传入请求数据。 此负载可以是查询字符串、表单数据和 JSON 对象的形式。 Flask 与任何其他 Web 框架一样,允许您访问请求数据。

在本教程中,您将构建一个 Flask 应用程序,其中包含三个接受查询字符串、表单数据或 JSON 对象的路由。

先决条件

要完成本教程,您需要:

  • 该项目需要在本地环境中安装Python。
  • 该项目将使用 Pipev,这是一种生产就绪工具,旨在将所有打包世界中最好的东西带入 Python 世界。 它将 Pipfile、pip 和 virtualenv 整合到一个命令中。
  • 测试 API 端点需要下载和安装类似 Postman 的工具。

本教程已使用 Pipenv v2020.11.15、Python v3.9.0 和 Flask v1.1.2 进行了验证。

设置项目

为了演示使用请求的不同方式,您需要创建一个 Flask 应用程序。 尽管示例应用程序对视图函数和路由使用了简化的结构,但您在本教程中学到的内容可以应用于任何组织视图的方法,例如基于类的视图、蓝图或 Flask-Via 之类的扩展。

首先,您需要创建一个项目目录。 打开终端并运行以下命令:

mkdir flask_request_example

然后,导航到新目录:

cd flask_request_example

接下来,安装 Flask。 打开终端并运行以下命令:

pipenv install Flask

pipenv 命令将为该项目创建一个 virtualenv、一个 Pipfile、安装 flask 和一个 Pipfile.lock。

要激活项目的 virtualenv,请运行以下命令:

pipenv shell

要访问 Flask 中的传入数据,您必须使用 request 对象。 request 对象包含来自请求的所有传入数据,其中包括 mimetype、referrer、IP 地址、原始数据、HTTP 方法和标头等。

尽管 request 对象保存的所有信息都可能有用,但就本文而言,您将重点关注通常由端点调用者直接提供的数据。

要访问 Flask 中的请求对象,您需要从 Flask 库中导入它:

from flask import request

然后,您可以在任何视图函数中使用它。

使用您的代码编辑器创建一个 app.py 文件。 导入 Flaskrequest 对象。 并为 query-exampleform-examplejson-example 建立路由:

应用程序.py

# import main Flask class and request object
from flask import Flask, request

# create the Flask app
app = Flask(__name__)

@app.route('/query-example')
def query_example():
    return 'Query String Example'

@app.route('/form-example')
def form_example():
    return 'Form Data Example'

@app.route('/json-example')
def json_example():
    return 'JSON Object Example'

if __name__ == '__main__':
    # run app in debug mode on port 5000
    app.run(debug=True, port=5000)

接下来,打开您的终端并使用以下命令启动应用程序:

python app.py

该应用程序将在端口 5000 上启动,因此您可以使用以下链接在浏览器中查看每条路线:

http://127.0.0.1:5000/query-example (or localhost:5000/query-example)
http://127.0.0.1:5000/form-example (or localhost:5000/form-example)
http://127.0.0.1:5000/json-example (or localhost:5000/json-example)

该代码建立了三个路由,访问每个路由会分别显示"Query String Example""Form Data Example""JSON Object Example"的消息。

使用查询参数

添加到查询字符串的 URL 参数是将数据传递到 Web 应用程序的常用方法。 在浏览网页时,您之前可能会遇到过查询字符串。

查询字符串类似于以下内容:

example.com?arg1=value1&arg2=value2

查询字符串在问号 (?) 字符之后开始:

example.com?arg1=value1&arg2=value2

并且键值对由 & 符号 (&) 字符分隔:

example.com?arg1=value1&arg2=value2

对于每一对,键后跟一个等号 (=) 字符,然后是值。

arg1 : value1
arg2 : value2

查询字符串对于传递不需要用户采取行动的数据很有用。 您可以在应用程序的某处生成查询字符串并将其附加到 URL,这样当用户发出请求时,数据会自动传递给他们。 使用 GET 作为方法的表单也可以生成查询字符串。

让我们在 query-example 路由中添加一个查询字符串。 在这个假设的示例中,您将提供将在屏幕上显示的编程语言的名称。 创建 "language" 的键和 "Python" 的值:

http://127.0.0.1:5000/query-example?language=Python

如果您运行应用程序并导航到该 URL,您将看到它仍然显示 "Query String Example" 消息。

您将需要对处理查询参数的部分进行编程。 此代码将使用 request.args.get('language')request.args['language'] 读入 language 键。

通过调用 request.args.get('language'),如果 URL 中不存在 language 键,应用程序将继续运行。 在这种情况下,该方法的结果将是 None

通过调用 request.args['language'],如果 URL 中不存在 language 键,应用程序将返回 400 错误。

在处理查询字符串时,建议使用 request.args.get() 以防止应用程序失败。

让我们读取 language 键并将其显示为输出。

修改app.py中的query-example路由,代码如下:

应用程序.py

@app.route('/query-example')
def query_example():
    # if key doesn't exist, returns None
    language = request.args.get('language')

    return '''<h1>The language value is: {}</h1>'''.format(language)

然后,运行应用程序并导航到 URL:

http://127.0.0.1:5000/query-example?language=Python

浏览器应显示以下消息:

OutputThe language value is: Python

来自 URL 的参数被分配给 language 变量,然后返回给浏览器。

要添加更多查询字符串参数,您可以将 & 符号和新的键值对附加到 URL 的末尾。 创建 "framework" 的键和 "Flask" 的值:

http://127.0.0.1:5000/query-example?language=Python&framework=Flask

如果您想要更多,请继续添加与号和键值对。 创建 "website" 的键和 "DigitalOcean" 的值:

http://127.0.0.1:5000/query-example?language=Python&framework=Flask&website=DigitalOcean

要访问这些值,您仍将使用 request.args.get()request.args[]。 让我们使用两者来演示缺少密钥时会发生什么。 修改 query_example 路由,将结果的值赋给变量,然后显示:

@app.route('/query-example')
def query_example():
    # if key doesn't exist, returns None
    language = request.args.get('language')

    # if key doesn't exist, returns a 400, bad request error
    framework = request.args['framework']

    # if key doesn't exist, returns None
    website = request.args.get('website')

    return '''
              <h1>The language value is: {}</h1>
              <h1>The framework value is: {}</h1>
              <h1>The website value is: {}'''.format(language, framework, website)

然后,运行应用程序并导航到 URL:

http://127.0.0.1:5000/query-example?language=Python&framework=Flask&website=DigitalOcean

浏览器应显示以下消息:

OutputThe language value is: Python
The framework value is: Flask
The website value is: DigitalOcean

从 URL 中删除 language 键:

http://127.0.0.1:5000/query-example?framework=Flask&website=DigitalOcean

当没有为 language 提供值时,浏览器应显示以下带有 None 的消息:

OutputThe language value is: None
The framework value is: Flask
The website value is: DigitalOcean

从 URL 中删除 framework 键:

http://127.0.0.1:5000/query-example?language=Python&website=DigitalOcean

浏览器应该遇到错误,因为它需要 framework 的值:

Outputwerkzeug.exceptions.BadRequestKeyError
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'framework'

现在您了解了处理查询字符串。 让我们继续下一个类型的传入数据。

使用表单数据

表单数据来自已作为 POST 请求发送到路由的表单。 因此,不会看到 URL 中的数据(除了使用 GET 请求提交表单的情况),表单数据将在后台传递给应用程序。 即使您无法轻松查看传递的表单数据,您的应用程序仍然可以读取它。

为了演示这一点,修改 app.py 中的 form-example 路由以接受 GET 和 POST 请求并返回一个表单:

应用程序.py

# allow both GET and POST requests
@app.route('/form-example', methods=['GET', 'POST'])
def form_example():
    return '''
              <form method="POST">
                  <div><label>Language: <input type="text" name="language"></label></div>
                  <div><label>Framework: <input type="text" name="framework"></label></div>
                  <input type="submit" value="Submit">
              </form>'''

然后,运行应用程序并导航到 URL:

http://127.0.0.1:5000/form-example

浏览器应该显示一个包含两个输入字段的表单——一个用于 language,一个用于 framework——和一个提交按钮。

关于这个表单,最重要的一点是它对生成表单的同一路由执行 POST 请求。 将在应用程序中读取的键都来自我们表单输入的 name 属性。 在这种情况下,languageframework 是输入的名称,因此您可以在应用程序中访问这些名称。

在视图函数中,您需要检查请求方法是 GET 还是 POST。 如果是 GET 请求,则可以显示表单。 否则,如果它是一个 POST 请求,那么您将需要处理传入的数据。

修改app.py中的form-example路由,代码如下:

应用程序.py

# allow both GET and POST requests
@app.route('/form-example', methods=['GET', 'POST'])
def form_example():
    # handle the POST request
    if request.method == 'POST':
        language = request.form.get('language')
        framework = request.form.get('framework')
        return '''
                  <h1>The language value is: {}</h1>
                  <h1>The framework value is: {}</h1>'''.format(language, framework)

    # otherwise handle the GET request
    return '''
           <form method="POST">
               <div><label>Language: <input type="text" name="language"></label></div>
               <div><label>Framework: <input type="text" name="framework"></label></div>
               <input type="submit" value="Submit">
           </form>'''

然后,运行应用程序并导航到 URL:

http://127.0.0.1:5000/form-example

Python 的值填写 language 字段,用 Flask 的值填写 framework 字段。 然后,按提交

浏览器应显示以下消息:

OutputThe language value is: Python
The framework value is: Flask

现在您了解了处理表单数据。 让我们继续下一个类型的传入数据。

使用 JSON 数据

JSON 数据通常由调用路由的进程构建。

示例 JSON 对象如下所示:

{
    "language" : "Python",
    "framework" : "Flask",
    "website" : "Scotch",
    "version_info" : {
        "python" : "3.9.0",
        "flask" : "1.1.2"
    },
    "examples" : ["query", "form", "json"],
    "boolean_test" : true
}

与查询字符串和表单数据相比,这种结构可以允许传递更复杂的数据。 在示例中,您会看到嵌套的 JSON 对象和项目数组。 Flask 可以处理这种格式的数据。

修改app.py中的form-example路由,接受POST请求,忽略GET等其他请求:

应用程序.py

@app.route('/json-example', methods=['POST'])
def json_example():
    return 'JSON Object Example'

与用于查询字符串和表单数据的 Web 浏览器不同,就本文而言,要发送 JSON 对象,您将使用 Postman 向 URL 发送自定义请求。

注意:如果您需要帮助导航 Postman 界面以获取请求,请参阅 官方文档


在 Postman 中,添加 URL 并将类型更改为 POST。 在正文选项卡上,更改为 raw 并从下拉列表中选择 JSON

这些设置是必需的,以便 Postman 可以正确发送 JSON 数据,因此您的 Flask 应用程序将了解它正在接收 JSON:

POST http://127.0.0.1:5000/json-example
Body
raw JSON

接下来,将前面的 JSON 示例复制到文本输入中。

发送请求,您应该得到 "JSON Object Example" 作为响应。 这是相当反气候的,但在意料之中,因为用于处理 JSON 数据响应的代码尚未编写。

要读取数据,您必须了解 Flask 如何将 JSON 数据转换为 Python 数据结构:

  • 任何作为对象的东西都会被转换为 Python 字典。 JSON 中的 {"key" : "value"} 对应于 somedict['key'],在 Python 中返回一个值。
  • JSON 中的数组被转换为 Python 中的列表。 由于语法相同,这里有一个示例列表:[1,2,3,4,5]
  • JSON 对象中引号内的值在 Python 中变为字符串。
  • 布尔值 truefalse 在 Python 中变为 TrueFalse
  • 最后,没有引号的数字在 Python 中变成了数字。

现在让我们编写代码来读取传入的 JSON 数据。

首先,让我们使用 request.get_json() 将 JSON 对象中的所有内容分配到一个变量中。

request.get_json() 将 JSON 对象转换为 Python 数据。 让我们将传入的请求数据分配给变量并通过对 json-example 路由进行以下更改来返回它们:

应用程序.py

# GET requests will be blocked
@app.route('/json-example', methods=['POST'])
def json_example():
    request_data = request.get_json()

    language = request_data['language']
    framework = request_data['framework']

    # two keys are needed because of the nested object
    python_version = request_data['version_info']['python']

    # an index is needed because of the array
    example = request_data['examples'][0]

    boolean_test = request_data['boolean_test']

    return '''
           The language value is: {}
           The framework value is: {}
           The Python version is: {}
           The item at index 0 in the example list is: {}
           The boolean value is: {}'''.format(language, framework, python_version, example, boolean_test)

请注意您如何访问不在顶层的元素。 使用 ['version']['python'] 是因为您正在输入嵌套对象。 并且 ['examples'][0] 用于访问示例数组中的第 0 个索引。

如果随请求发送的 JSON 对象没有在您的视图函数中访问的键,则请求将失败。 如果您不希望它在密钥不存在时失败,则必须在尝试访问它之前检查该密钥是否存在。

应用程序.py

# GET requests will be blocked
@app.route('/json-example', methods=['POST'])
def json_example():
    request_data = request.get_json()

    language = None
    framework = None
    python_version = None
    example = None
    boolean_test = None

    if request_data:
        if 'language' in request_data:
            language = request_data['language']

        if 'framework' in request_data:
            framework = request_data['framework']

        if 'version_info' in request_data:
            if 'python' in request_data['version_info']:
                python_version = request_data['version_info']['python']

        if 'examples' in request_data:
            if (type(request_data['examples']) == list) and (len(request_data['examples']) > 0):
                example = request_data['examples'][0]

        if 'boolean_test' in request_data:
            boolean_test = request_data['boolean_test']

    return '''
           The language value is: {}
           The framework value is: {}
           The Python version is: {}
           The item at index 0 in the example list is: {}
           The boolean value is: {}'''.format(language, framework, python_version, example, boolean_test)

运行应用程序并使用 Postman 提交示例 JSON 请求。 在响应中,您将获得以下输出:

OutputThe language value is: Python
The framework value is: Flask
The Python version is: 3.9
The item at index 0 in the example list is: query
The boolean value is: false

现在您了解了处理 JSON 对象。

结论

在本文中,您构建了一个 Flask 应用程序,其中包含三个接受查询字符串、表单数据或 JSON 对象的路由。

此外,请记住,所有方法都必须解决在缺少密钥时优雅失败的反复考虑。

警告: 本文未涉及的一个主题是清理用户输入。 清理用户输入将确保应用程序读取的数据不会导致意外失败或绕过安全措施。


如果您想了解有关 Flask 的更多信息,请查看 我们的 Flask 主题页面 以获取练习和编程项目。