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

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

Все дело в куки и сессиях.

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

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

Куки представляют собой данные, которые сервер сохраняет в браузере. Процесс работы с ними выглядит так:

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

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

Во Flask для установки куки используется метод 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/ для просмотра всех сохраненных куки.

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

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

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

Важно: если куки уже настроены, каждый запрос к http://localhost:5000/cookie/ обновляет срок их действия.

Доступ к куки

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

#...
@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 страница будет отображаться с шрифтом по умолчанию. Если выбрать другой шрифт через выпадающее меню, форму отправится, и куки font с новым значением будут обновлены на 15 дней. Пользователь перенаправится обратно на страницу http://localhost:5000/article, уже с выбранным шрифтом.

При повторном посещении страницы http://localhost:5000/article она будет отображаться с тем шрифтом, который был выбран ранее.

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

Таким образом, после выбора нового шрифта, страница обновится и отобразит содержимое в новом стиле.

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

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

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

  1. Куки не считаются безопасными. Данные в них доступны всем, поэтому не следует хранить в куки пароли, информацию о банковских картах и другие важные данные.
  2. Пользователи могут отключить куки. Большинство браузеров позволяют деактивировать куки, при этом никакого предупреждения не предоставляется. Решить проблему можно простейшим кодом на JavaScript, который будет уведомлять пользователя о необходимости включить куки для корректной работы сайта.
<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 КБ данных.

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

Вопросы для самопроверки:

  1. Какую функцию выполняет метод `set_cookie()` во Flask?
  2. Как проверить наличие и значение куки в Flask-приложении?
  3. Как удалить куки в Flask?
  4. Что произойдет, если пользователь отключит использование куки в браузере?
  5. Какое ограничение существует на размер и количество куки?