Сообщить об ошибке.

Получение данных из запроса в приложении на Flask.

В материале рассматривается работа с контекстом запроса в приложении на Flask и доступ к различным данным запроса, а именно доступ к полям формы, дополнительным параметрам URL-адреса, получение данных JSON, извлечения информации о User-Agent и IP-адреса клиента, получение referrer-URL и так далее.

Содержание:


Доступ к параметрам, передаваемых в URL GET запросом.

Параметры GET запроса, передаются в качестве параметров URL-адреса. Это та часть URL- адреса, которая расположена после знака вопроса, например http://example.ru/search?query=str.lower&opt=9, параметрами запроса будут считаться query=str.lower&opt=9. Такие адреса в основном используются в поисковых запросах, REST API сайта или в AJAX-запросах.

Для получения этих параметров необходимо воспользоваться свойством args входящего запроса flask.Request. По умолчанию, свойство request.args, представляет собой объект словаря, доступ к значениям параметров осуществляется по ключу request.args['opt'] Для безопасного извлечения ключей из словаря, что бы избежать исключений (при отсутствии ключа), можно воспользоваться методом dict.get(). Во Flask, синтаксис этого метода выглядит следующим образом:

request.args.get(key, default=None, type=None)

Где аргумент key - это ключ (имя параметра в URL); default - возвращаемое значение, если ключа key не существует. Пример извлечения параметров GET-запроса, если URL-адрес имеет вид http://example.ru/search?query=str.lower&opt=9:

from flask import request

@app.route('/search/', methods=['GET'])
def search():
    error = None
    query = request.args.get('query')
    # проверяем, передается ли параметр
    # 'query' в URL-адресе
    if query and query != '':
        # если `query`существует и это не пустая строка,
        # то можно приступать к обработке запроса
        opt = request.args.get('opt', default=0, type=int)
        # возвратит `query=str.lower, opt=9`
        return f"query={query}, opt={opt}"
    else:
        # если `query` не существует или это пустая строка, то 
        # отображаем форму поискового запроса с сообщением.
        error = 'Не введен запрос!'
        return render_template('search.html', error=error)

Если важно сохранение порядка передаваемых параметров, то можно изменить тип данных, присвоив request.parameter_storage_class другой тип, например werkzeug.datastructures.ImmutableList.

Доступ к данным формы, передаваемой POST запросом.

Для доступа к данным формы (передаваемым в запросах POST или PUT) нужно использовать атрибут входящего запроса request.form, который так же представляет собой словароподобный объект. Ключи request.form имеют значения атрибута name HTML-тэга <input> в форме (например <input name="username">).

Так как request.form представляет собой словароподобный объект, то к нему рекомендуется обращаться с помощью метода словаря dict.get() или путем перехвата KeyError и выдачи собственной страницы с ошибкой HTTP 404 Not Found.

В примере, для извлечения полей формы, воспользуемся методом request.args.get(key, default=None, type=None). Предположим, что была передана форма авторизации пользователя на сайте:

from flask import request

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form.get('username'),
                       request.form.get('password')):
            # если условие if выполнилось успешно, то 'username' 
            # существует и дальше к нему можно обращаться как  
            # к ключу словаря, что на много быстрее.
            return log_the_user_in(request.form['username'])
        else:
            error = 'Неверное имя пользователя или пароль'
    return render_template('login.html', error=error)

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

Примечание: объект запроса flask.Request еще содержит атрибут request.values, который объединяет в себе значения, передаваемые GET и POST запросами. Другими словами, используя request.values.get() можно получить доступ как к элементам передаваемой формы в HTML-полях <input> , так и параметрам, передаваемым в URL-адресе.

Обратите внимание, что атрибут объекта запроса request.method содержит в себе строковое значение HTTP-метода.

Получение текущего URL-адреса страницы.

Так как URL-адрес может иметь переменные части, которые указываются в угловых скобках, иногда (например, в целях сбора статистики) необходимо получить его оригинал, что бы потом, в функции-представлении, не воссоздавать его вручную.

Объект запроса flask.Request позволяет это сделать. За URL-адрес в объекте запроса отвечают несколько атрибутов:

  • request.url - полный URL-адрес запроса со схемой request.scheme, хостом request.host_url, корневым путем request.root_path, путем request.path и строкой запроса request.query_string.
  • request.base_url - как request.url, но без строки запроса.
  • request.url_root - URL-адрес со схемой request.scheme, хостом request.host_url, корневым путем request.root_path.
  • request.scheme - схема URL-адресов (http или https).
  • request.host - только хост, включая порт, если он доступен.
  • request.root_path - префикс, под которым монтируется приложение, без завершающей косой черты (еще известен как environ["SCRIPT_ROOT"]).
  • request.path - часть пути URL-адреса после root_path. Это путь, используемый для маршрутизации внутри приложения.
  • request.full_path - запрошенный путь URL, включая строку запроса (параметры URL, после знака вопроса).
  • request.query_string - часть URL-адреса после знака вопроса '?'.
  • request.url_charset - кодировка, которая предполагается для URL-адресов.

Разберем значение приведенных выше атрибутов объекта запроса на примере. Допустим, что приложение имеет следующий корень: http://www.example.com/myapp и пользователь запрашивает следующий URI-адрес: http://www.example.com/myapp/%CF%80/page.html?x=y

В этом случае значения вышеупомянутых атрибутов будут следующими:

  • request.url: 'http://www.example.com/myapp/π/page.html?x=y';
  • request.base_url: 'http://www.example.com/myapp/π/page.html';
  • request.url_root: 'http://www.example.com/myapp/'
  • request.scheme: 'http';
  • request.host: 'www.example.com';
  • request.root_path: '/myapp';
  • request.path: '/π/page.html';
  • request.full_path: '/π/page.html?x=y';
  • request.query_string: x=y.

Информация об окружении WSGI сервера.

Если запуск и работоспособность приложение Flask обеспечивается WSGI сервером (например gunicorn), то информацию об окружении этого сервера можно получить из атрибута объекта запроса request.environ. Этот атрибут представляет собой словарный объект и содержит информацию об окружении WSGI, а так же HTTP-заголовки, полученные вместе с запросом.

Реальный IP-адрес клиента.

Переменные WSGI сервера необходимы, например, что бы узнать реальный IP-адрес посетителя сайта, если приложение Flask работает за прокси сервером, таким как Nginx. В ситуации с прокси, атрибут запроса request.remote_addr будет возвращать IP-адрес самого прокси сервера (т. е. локальный IP-адрес, на котором он работает, например 127.0.0.1), а не клиента. Реальный IP-адрес клиента можно узнать из окружения WSGI, обратившись к ключу 'HTTP_X_FORWARDED_FOR' или 'X-Real-IP', это зависит, какая переменная использовалась при настройке Nginx:

# если `HTTP_X_FORWARDED_FOR` не определен, 
# то просто возвращаем `request.remote_addr`
request.environ.get('HTTP_X_FORWARDED_FOR', request.remote_addr)

Примечание. Заголовок X-Forwarded-For (XFF) является де-факто стандартным заголовком для идентификации происхождения IP-адреса клиента, подключающегося к веб-серверу через HTTP-прокси или балансировщик нагрузки. Когда трафик перехватывается между клиентом и сервером, то журналы доступа к серверу содержат только IP-адрес прокси-сервера или балансировки нагрузки.

И вообще, при связке: прокси сервер -> WSGI сервер -> приложение Flask, максимум информации лучше брать из окружения request.environ, т.к. здесь она будет более правдива.

Переменные окружения WSGI сервера.

Из словарного атрибута запроса request.environ можно узнать очень много интересного, например реферальный URL-адрес (это адрес, с которого клиент перешел на текущий URL), его ключ в окружении 'HTTP_REFERER'. Возвращаемые ключи словарного объекта request.environ во многом зависят от того где работает и как настроен WSGI сервер, т.е. здесь все индивидуально.

Вот некоторые из них:

'wsgi.version', 'PATH_INFO''HTTP_SEC_CH_UA'
'wsgi.url_scheme''QUERY_STRING''HTTP_SEC_CH_UA_MOBILE'
'wsgi.input''REQUEST_URI''HTTP_SEC_CH_UA_PLATFORM'
'wsgi.errors''RAW_URI''HTTP_UPGRADE_INSECURE_REQUESTS'
'wsgi.multithread''REMOTE_ADDR''HTTP_DNT'
'wsgi.multiprocess''REMOTE_PORT''HTTP_USER_AGENT'
'wsgi.run_once''SERVER_NAME''HTTP_ACCEPT'
'werkzeug.socket''SERVER_PORT''HTTP_REFERER'
'SERVER_SOFTWARE''SERVER_PROTOCOL''HTTP_ACCEPT_ENCODING'
'REQUEST_METHOD''HTTP_HOST''HTTP_ACCEPT_LANGUAGE'
'SCRIPT_NAME''HTTP_CONNECTION''HTTP_COOKIE'

Только не забывайте при обращении к словарному атрибуту request.environ, применять метод dict.get(), что бы избежать появления исключения, если какой-то ключ отсутствует.

Получение данных в формате JSON.

Что бы извлечь JSON данные, передаваемые в запросе необходимо воспользоваться методом объекта запроса .get_json(), который имеет следующий синтаксис:

data = request.get_json(force=False, silent=False, cache=True)

Принимаемые аргументы:

  • force: (bool) - игнорирует тип mimetype и всегда пытается анализировать JSON.
  • silent: (bool) - отключает появление возможных ошибок при анализе JSON и если они происходят то возвращает None.
  • cache: (bool) - сохраняет проанализированный JSON для последующих вызовов.

Если заголовок mimetype не указывает на JSON данные (application/json), то этот метод, по умолчанию (force=False), возвратит None.

Если синтаксический анализ JSON завершается неудачно, то вызывается функция request.on_json_loading_failed(), и в качестве возвращаемого значения используется ее значение.

Другие полезные атрибуты объекта запроса.

request.headers:

Атрибут объекта запроса request.headers содержит все заголовки, полученные вместе с запросом. Представляет собой словарный объект, следовательно, для безопасного получения заголовка можно воспользоваться методом словаря dict.get():

request.headers.get('HEADER-NAME')

Если заголовок с именем 'HEADER-NAME' не существует то код выше вернет None (исключения не будет), поэтому его можно использовать в конструкции:

if request.headers.get('HEADER-NAME'):
    ...

Но прямое обращение к ключу приведет к появлению исключения KeyError: 'HEADER-NAME'

if request.headers['HEADER-NAME']:
    ...

Если необходимо обращаться к request.headers без использования метода dict.get(), то используйте следующую конструкцию:

if 'HEADER-NAME' in request.headers:
   customHeader = request.headers['HEADER-NAME']
    ...

request.cookies:

Атрибут объекта запроса request.cookies содержит все файлы cookie, переданных вместе с запросом. Представляет собой словарный объект.

cookie = request.cookies.get('cookie-name'):

Методику работы с объектом request.cookies смотрите на примере request.headers

request.referrer:

Атрибут объекта запроса request.referrer содержит поле заголовка запроса, которое позволяет клиенту указать, в интересах сервера, адрес ресурса, с которого клиент перешел на текущий URL.

referrer = request.referrer

# что эквивалентно получению заголовка

referrer = request.headers.get.('Referer')

request.user_agent:

Атрибут объекта запроса request.user_agent представляет собой данные заголовка, содержащие сведения о пользовательском агенте.

Чтобы получить значение заголовка, необходимо использовать выражение: request.user_agent.string.

agent_string = request.user_agent.string

# что эквивалентно получению заголовка

request.headers.get('User-Agent')