Обработка данных входящих запросов в Flask

Введение

Веб-приложениям часто требуется обрабатывать данные входящих запросов пользователей. Эта полезная нагрузка может иметь форму строк запросов, данных форм и объектов JSON. Flask, как и любая другая веб-инфраструктура, предоставляет доступ к данным запросов.

В этом учебном модуле мы создадим приложение Flask с тремя маршрутами, которое будет принимать строки запросов, данные форм и объекты JSON.

Предварительные требования

Для данного обучающего руководства вам потребуется следующее:

  • Для данного проекта потребуется установить Python в локальной среде.
  • В этом проекте мы будем использовать инструмент Pipenv, предоставляющий отличные возможности упаковки для программирования на 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, install flask и Pipfile.lock.

Для активации virtualenv этого проекта запустите следующую команду:

  • pipenv shell

Чтобы получить доступ к входящим данным в Flask, вам нужно будет использовать объект request. В объекте request хранятся все входящие данные запроса, включая тип MIME, источник, IP-адрес, необработанные данные, метод HTTP, заголовки и т. д.

Хотя вся информация в объекте request может быть полезной для наших целей, в этом учебном модуле мы уделим основное внимание данным, которые обычно предоставляются вызывающей стороной напрямую.

Чтобы получить доступ к запрашиваемому объекту в Flask, вам потребуется импортировать его из библиотеки Flask:

from flask import request 

После этого у вас появится возможность использовать его в любых ваших функциях представлений.

Используйте редактор кода для создания файла app.py. Импортируйте Flask и объект request. Также установите маршруты для query-example, form-example и json-example:

app.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, добавляемые в строку запроса, часто используются для передачи данных в веб-приложение. Возможно, вам уже приходилось видеть строку запроса на веб-страницах.

Строка запроса выглядит следующим образом:

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".

Вам нужно будет программировать часть, обрабатывающую аргументы запроса. Этот код считает ключ language, используя request.args.get('language') или request.args['language'].

При вызове request.args.get('language') приложение продолжит работу, если ключ language отсутствует по указанному URL. В этом случае данный метод будет иметь результат None.

При вызове request.args['language'] приложение возвращает ошибку 400, если ключ language отсутствует по указанному URL.

При работе со строками запросов, рекомендуется использовать request.args.get(), чтобы предотвратить возможные сбои в работе приложения.

Давайте прочитаем ключ language и выведем его.

Измените маршрут query-example в app.py с помощью следующего кода:

app.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 

Удалите ключ language из URL:

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

Браузер должен вывести следующее сообщение со словом None, если для language будет отсутствовать значение:

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

Удалите ключ framework из URL:

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) данные форм передаются приложению незаметно. Хотя вы не видите передаваемые данные форм, ваше приложение может их считывать.

Чтобы продемонстрировать это, давайте изменим маршрут form-example в app.py так, чтобы принимать запросы GET и POST и возвращать форму:

app.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 в полях ввода формы. В этом случае language и framework являются именами полей ввода, и поэтому у вас будет доступ к ним в приложении.

Внутри функции просмотра вам нужно будет проверить метод запроса: GET или POST. Если это запрос GET, вы можете вывести форму. В противном случае это запрос POST, и вам нужно обработать входящие данные.

Измените маршрут form-example в app.py, добавив следующий код:

app.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 

Введите в поле language значение Python, а в поле framework — значение Flask. Затем нажмите кнопку Submit.

Браузер должен вывести следующее сообщение:

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.

Измените маршрут form-example в app.py, чтобы принимать запросы POST и игнорировать другие запросы, в частности GET:

app.py

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

Для строк запросов и данных форм в этом учебном модуле мы использовали браузер, но для отправки объекта JSON мы используем Postman, чтобы отправлять персонализированные запросы в URL.

Примечание. Если вам нужна помощь в навигации по интерфейсу Postman для отправки запросов, воспользуйтесь официальной документацией.

Добавьте URL в Postman и измените тип на POST. На вкладке body переключитесь на 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. {"key" : "value"} в JSON соответствуют somedict['key'], который возвращает значение в Python.
  • Массив в JSON конвертируется в список в Python. Поскольку синтаксис одинаковый, приведем список примеров: [1,2,3,4,5]
  • Значения в кавычках объекта JSON станут строками в Python.
  • Логические операторы true и false становятся True и False в Python.
  • Числа без кавычек становятся числами в Python.

Теперь давайте поработаем с кодом, чтобы считывать входящие данные JSON.

Вначале добавим все содержимое объекта JSON в переменную, используя request.get_json().

request.get_json() конвертирует объект JSON в данные Python. Давайте назначим данные входящего запроса в переменные и выведем их, внеся следующие изменения в маршрут json-example:

app.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 в массиве example.

Если объект JSON, отправленный с запросом, не имеет ключа, доступ к которому осуществляется через функцию view, запрос не будет выполняться. Если вы не хотите, чтобы запрос выдавал сбой при отсутствии ключа, вам нужно будет проверить наличие ключа, прежде чем пытаться получить к нему доступ.

app.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) 

Запустите приложение и отправьте пример запроса JSON с помощью Postman. В ответе вы увидите следующее:

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 вы найдете полезные упражнения и проекты по программированию.