#12 Куки во Flask

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

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

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

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

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

Стоит отметить, что когда куки настроены, последующие запросы к https://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
#...

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

Если открыть https://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
#...

Если сейчас зайти на https://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>

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

При посещении https://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 КБ.

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