В материале рассматривается работа с контекстом запроса в приложении на Flask и доступ к различным данным запроса, а именно доступ к полям формы, дополнительным параметрам URL-адреса, получение данных JSON, извлечения информации о User-Agent
и IP-адреса клиента, получение referrer-URL и так далее.
GET
запросом.POST
запросом.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-адрес может иметь переменные части, которые указываются в угловых скобках, иногда (например, в целях сбора статистики) необходимо получить его оригинал, что бы потом, в функции-представлении, не воссоздавать его вручную.
Объект запроса 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.Если запуск и работоспособность приложение Flask обеспечивается WSGI сервером (например gunicorn
), то информацию об окружении этого сервера можно получить из атрибута объекта запроса request.environ
. Этот атрибут представляет собой словарный объект и содержит информацию об окружении WSGI, а так же HTTP-заголовки, полученные вместе с запросом.
Из словарного атрибута запроса 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()
, что бы избежать появления исключения, если какой-то ключ отсутствует.
Переменные 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
, т.к. здесь она будет более правдива.
Что бы извлечь 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')