До этого момента все созданные в уроках страницы были очень простыми. Браузер отправляет запрос на сервер, сервер отвечает HTML-страницей, и это все. HTTP — это протокол, который не сохраняет свое состояние. Это значит, что в HTTP нет встроенных способов сообщить серверу, что оба запроса поступили от одного и того же пользователя. В результате сервер не знает, пытается ли пользователь получить доступ к странице впервые или в тысячный раз. Он обслуживает каждого так, будто бы это первое обращение к странице.
Если попробовать зайти в любой интернет-магазин и поискать определенные товары, то при следующем посещении сайт будет предлагать рекомендации, основанные на примерах прошлых поисков. Как же получается, что сайт узнает конкретного пользователя?
Ответ — куки и сессии.
Этот урок посвящен куки, а о сессиях речь пойдет в следующем.
Что такое куки?
Куки — это всего лишь фрагмент данных, которые сервер устанавливает в браузере. Вот как это работает:
- Браузер отправляет запрос на получение веб-страницы от сервера.
- Сервер отвечает на запрос, отправляя запрошенную страницу вместе с одним или несколькими куки.
- При получении ответа браузер рендерит страницу и сохраняет куки на компьютере пользователя.
- Последующий запрос на сервер будет включать информацию из куки в заголовке
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/
.
С этого момента куки 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/
сейчас, отобразится страница со следующим содержимым.
Объект 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/
, отобразится следующий ответ:
Теперь, понимая как работают куки, можно изучить следующие примеры кода и получить реальные практические примеры того, как настраивать куки для сохранения пользовательских предпочтений.
Добавим следующий код после функции представления 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
станица отобразится со шрифтом по умолчанию.
Но если выбрать новый шрифт из выпадающего меню, шрифт страницы поменяется на выбранный ранее.
Недостатки куки
Перед использованием куки в реальном проекте нужно знать об их недостатках.
- Куки небезопасны. Данные, которые в них хранятся, видны всем, поэтому в куки нельзя хранить пароли, данные банковских карт и так далее.
- Куки можно отключить. Большинство браузеров дают пользователям возможность отключить куки. Если это происходит, никакого предупреждения нет. Чтобы бороться с этой проблемой, можно использовать, например, такой простейший JS-код, чтобы уведомить пользователя о том, что куки должны быть включены для корректной работы.
<script>
document.cookie = "foo=bar;";
if (!document.cookie)
{
alert("This website requires cookies to function properly");
}
</script>
- Каждая порция куки хранит не более 4 КБ данных. Помимо этого браузеры накладывают ограничения на то, сколько куки может установить сайт. Лимиты варьируются от 30 до 50.
- Куки отправляются с каждым запросом к серверу. Если предположить, что у сайта 20 куки размером 4 КБ, то каждый запрос будет давать дополнительную нагрузку в размере 80 КБ.
Некоторые из этих проблем можно решить с помощью сессий, речь о которых пойдет в следующем уроке.