#12 Куки во Flask

1575

До этого момента все созданные в уроках страницы были очень простыми. Браузер отправляет запрос на сервер, сервер отвечает HTML-страницей, и это все. HTTP — это протокол, который не сохраняет свое состояние. Это значит, что в HTTP нет встроенных способов сообщить серверу, что оба запроса поступили от одного и того же пользователя. В результате сервер не знает, пытается ли пользователь получить доступ к странице впервые или в тысячный раз. Он обслуживает каждого так, будто бы это первое обращение к странице.

Если попробовать зайти в любой интернет-магазин и поискать определенные товары, то при следующем посещении сайт будет предлагать рекомендации, основанные на примерах прошлых поисков. Как же получается, что сайт узнает конкретного пользователя?

Ответ — куки и сессии.

Этот урок посвящен куки, а о сессиях речь пойдет в следующем.

Что такое куки?

Куки — это всего лишь фрагмент данных, которые сервер устанавливает в браузере. Вот как это работает:

  1. Браузер отправляет запрос на получение веб-страницы от сервера.
  2. Сервер отвечает на запрос, отправляя запрошенную страницу вместе с одним или несколькими куки.
  3. При получении ответа браузер рендерит страницу и сохраняет куки на компьютере пользователя.
  4. Последующий запрос на сервер будет включать информацию из куки в заголовке Cookie. Так будет продолжаться, пока не истечет сроки куки. Как только это происходит, куки удаляется из браузера.

Настройка куки

Во Flask для настройки куки используется метод объекта ответа set_cookie(). Синтаксис set_cookie() следующий:

set_cookie(key, value="", max_age=None)

key — обязательный аргумент, это название куки. value — данные, которые нужно сохранить в куки. По умолчанию это пустая строка. max_age — это срок действия куки в секундах. Если не указать срок, срок истечет при закрытии браузера пользователем.

Откроем main2.py, чтобы добавить следующий код после функции представления contact():

from flask import Flask, render_template, request, redirect, url_for, flash, make_response
#...
@app.route('/cookie/')
def cookie():
    res = make_response("Setting a cookie")
    res.set_cookie('foo', 'bar', max_age=60*60*24*365*2)
    return res
#...

Это пример создание куки под названием foo со значением bar, срок которых — 2 года.

Нужно запустить сервер и зайти на http://localhost:5000/cookie/. В качестве ответа откроется страница “Setting a cookie”. Чтобы посмотреть куки, настроенные сервером, нужно открыть инспектор хранилища в Firefox, нажав Shift+F9. Новое окно откроется в нижней части браузера. С левой стороны необходимо выбрать тип хранилища “Cookies” и нажать http://localhost:5000/, чтобы посмотреть все куки, настроенные сервером для http://localhost:5000/.

Инспектор хранилища в Firefox

С этого момента куки foo будут отправляться вместе с запросом на сервер http://localhost:5000/. Убедиться в этом можно с помощью сетевого монитора в Firefox. Он открывается сочетанием Ctrl+Shift+E. В мониторе нужно открыть http://localhost:5000/. В списке запросов с левой стороны — выбрать первый запрос, чтобы в правой панели отобразились его подробности:

Подробная информация о запросе

Стоит отметить, что когда куки настроены, последующие запросы к http://localhost:5000/cookie/ будут обновлять срок куки.

Доступ к куки

Для доступа к куки используется атрибут cookie объекта request. cookie — это атрибут типа словарь, содержащий все куки, отправленные браузером. Снова откроем main2.py, чтобы изменить функцию представления cookie():

#...
@app.route('/cookie/')
def cookie():
    if not request.cookies.get('foo'):
	res = make_response("Setting a cookie")
	res.set_cookie('foo', 'bar', max_age=60*60*24*365*2)
    else:
	res = make_response("Value of cookie foo is {}".format(request.cookies.get('foo')))
    return res
#...

Функция представления изменена таким образом, чтобы страница показывала значение куки, если они есть. Если нет — они будут настроены автоматически.

Если открыть http://localhost:5000/cookie/ сейчас, отобразится страница со следующим содержимым.

Значение куки во Flask

Объект request также доступен внутри шаблона. Это значит, что доступ к куки можно получить и с помощью кода Python. Подробнее об в этом в одном из следующих разделов.

Удаление куки

Чтобы удалить куки, нужно вызвать метод set_cookie() с названием куки, любым значением и указать срок max_age=0. В файле main2.py это можно сделать, добавив следующий код после функции представления cookie().

#...
@app.route('/delete-cookie/')
def delete_cookie():
    res = make_response("Cookie Removed")
    res.set_cookie('foo', 'bar', max_age=0)
    return res
#...

Если сейчас зайти на http://localhost:5000/delete-cookie/, отобразится следующий ответ:

Удаление куки во Flask

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

Добавим следующий код после функции представления delete_cookie() в файле main2.py.

#...
@app.route('/article/', methods=['POST',  'GET'])
def article():
    if request.method == 'POST':
	print(request.form)
	res = make_response("")
	res.set_cookie("font", request.form.get('font'), 60*60*24*15)
	res.headers['location'] = url_for('article')
	return res, 302

    return render_template('article.html')
#...

Далее нужно создать новый шаблон article.html со следующим кодом:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Article</title>
</head>
<body style="{% if request.cookies.get('font') %}font-family:{{ request.cookies.get('font') }}{% endif %}">

Select Font Preference: <br>
<form action="" method="post">
    <select name="font" onchange="submit()">
	<option value="">----</option>
	<option value="consolas" {% if request.cookies.get('font') == 'consolas' %}selected{% endif %}>consolas</option>
	<option value="arial" {% if request.cookies.get('font') == 'arial' %}selected{% endif %}>arial</option>
	<option value="verdana" {% if request.cookies.get('font')== 'verdana' %}selected{% endif %}>verdana</option>
    </select>
</form>

<h1>Festus, superbus toruss diligenter tractare de brevis, dexter olla.</h1>

<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam blanditiis debitis doloribus eos magni minus odit, provident tempora. Expedita fugiat harum in incidunt minus nam nesciunt voluptate. Facilis nesciunt, similique!
</p>

<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias amet animi aperiam inventore molestiae quos, reiciendis voluptatem. Ab, cum cupiditate fugit illo incidunt ipsa neque quam, qui quidem vel voluptatum.</p>


</body>
</html>

При первом посещении http://localhost:5000/article страница отобразится со шрифтом по умолчанию. Если пользователь поменяет шрифт с помощью выпадающего меню, будет отправлена форма. Значение условия if request.method == 'POST' станет истинным, и будут настроены куки font со значением выбранного шрифта, срок которых истечет через 15 дней, а пользователь будет перенаправлен на страницу http://localhost:5000/article, которая отобразится с новым выбранным шрифтом.

При посещении http://localhost:5000/article станица отобразится со шрифтом по умолчанию.

Сохранение шрифта в куки во Flask

Но если выбрать новый шрифт из выпадающего меню, шрифт страницы поменяется на выбранный ранее.

Сохраненный шрифт в куках во Flask

Недостатки куки

Перед использованием куки в реальном проекте нужно знать об их недостатках.

  1. Куки небезопасны. Данные, которые в них хранятся, видны всем, поэтому в куки нельзя хранить пароли, данные банковских карт и так далее.
  2. Куки можно отключить. Большинство браузеров дают пользователям возможность отключить куки. Если это происходит, никакого предупреждения нет. Чтобы бороться с этой проблемой, можно использовать, например, такой простейший JS-код, чтобы уведомить пользователя о том, что куки должны быть включены для корректной работы.
<script>
    document.cookie = "foo=bar;";
    if (!document.cookie)
    {
	alert("This website requires cookies to function properly");
    }
</script>
  1. Каждая порция куки хранит не более 4 КБ данных. Помимо этого браузеры накладывают ограничения на то, сколько куки может установить сайт. Лимиты варьируются от 30 до 50.
  2. Куки отправляются с каждым запросом к серверу. Если предположить, что у сайта 20 куки размером 4 КБ, то каждый запрос будет давать дополнительную нагрузку в размере 80 КБ.

Некоторые из этих проблем можно решить с помощью сессий, речь о которых пойдет в следующем уроке.

Тест на знание python

Что выведет этот код?
Какой будет результат выполнения этого кода?
Что выведет этот код?
Какая функция удаляет объект из списка?
Что выведет этот код?
Викиум